/* Ousía Copyright (C) 2014, 2015 Benjamin Paaßen, Andreas Stöckel This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #include #include #include #include #include #include #include #include #include #include #include "DocumentHandler.hpp" #include "State.hpp" namespace ousia { namespace parser_stack { /* DocumentHandler */ bool DocumentHandler::start(Variant::mapType &args) { Rooted document = context().getProject()->createDocument(args["name"].asString()); document->setLocation(location()); scope().push(document); scope().setFlag(ParserFlag::POST_HEAD, false); return true; } void DocumentHandler::end() { scope().pop(logger()); } /* DocumentChildHandler */ void DocumentChildHandler::preamble(Rooted &parentNode, size_t &fieldIdx, DocumentEntity *&parent) { // Check if the parent in the structure tree was an explicit field // reference. if (parentNode->isa(&RttiTypes::DocumentField)) { fieldIdx = parentNode.cast()->fieldIdx; parentNode = scope().selectOrThrow( {&RttiTypes::StructuredEntity, &RttiTypes::AnnotationEntity}); } // Reference the parent entity explicitly. parent = nullptr; if (parentNode->isa(&RttiTypes::StructuredEntity)) { parent = static_cast( parentNode.cast().get()); } else if (parentNode->isa(&RttiTypes::AnnotationEntity)) { parent = static_cast( parentNode.cast().get()); } } void DocumentChildHandler::createPath(const NodeVector &path, DocumentEntity *&parent, size_t p0) { size_t S = path.size(); for (size_t p = p0; p < S; p = p + 2) { // add the field. Rooted field{new DocumentField( manager(), scope().getLeaf(), parent->getDescriptor()->getFieldDescriptorIndex(), true)}; scope().push(field); // add the transparent/implicit structure element. Rooted transparent = parent->createChildStructuredEntity(path[p].cast(), Variant::mapType{}, path[p - 1]->getName(), ""); transparent->setLocation(location()); transparent->setTransparent(true); scope().push(transparent); parent = static_cast(transparent.get()); } // add the last field. Rooted field{new DocumentField( manager(), scope().getLeaf(), parent->getDescriptor()->getFieldDescriptorIndex(), true)}; scope().push(field); } void DocumentChildHandler::createPath(const size_t &firstFieldIdx, const NodeVector &path, DocumentEntity *&parent) { // Add the first element Rooted transparent = parent->createChildStructuredEntity( path[0].cast(), firstFieldIdx); transparent->setLocation(location()); transparent->setTransparent(true); scope().push(transparent); parent = static_cast(transparent.get()); createPath(path, parent, 2); } bool DocumentChildHandler::start(Variant::mapType &args) { // extract the special "name" attribute from the input arguments. // the remaining attributes will be forwarded to the newly constructed // element. std::string nameAttr; { auto it = args.find("name"); if (it != args.end()) { nameAttr = it->second.asString(); args.erase(it); } } scope().setFlag(ParserFlag::POST_HEAD, true); while (true) { Rooted parentNode = scope().getLeaf(); Rooted entity; // handle the root note specifically. if (parentNode->isa(&RttiTypes::Document)) { Rooted strct = scope().resolve( Utils::split(name(), ':'), logger()); if (strct == nullptr) { // if we could not resolve the name, throw an exception. throw LoggableException( std::string("\"") + name() + "\" could not be resolved.", location()); } entity = parentNode.cast()->createRootStructuredEntity( strct, args, nameAttr); } else { assert(parentNode->isa(&RttiTypes::DocumentField)); size_t fieldIdx; DocumentEntity *parent; preamble(parentNode, fieldIdx, parent); // TODO: REMOVE std::string thisName = name(); std::string parentClassName; if (parent != nullptr) { parentClassName = parent->getDescriptor()->getName(); } /* * Try to find a FieldDescriptor for the given tag if we are not in * a field already. This does _not_ try to construct transparent * paths in between. */ { ssize_t newFieldIdx = parent->getDescriptor()->getFieldDescriptorIndex(name()); if (newFieldIdx != -1) { Rooted field{new DocumentField( manager(), parentNode, newFieldIdx, false)}; field->setLocation(location()); scope().push(field); isExplicitField = true; return true; } } // Otherwise create a new StructuredEntity // TODO: Consider Anchors and AnnotationEntities Rooted strct = scope().resolve( Utils::split(name(), ':'), logger()); if (strct == nullptr) { // if we could not resolve the name, throw an exception. throw LoggableException( std::string("\"") + name() + "\" could not be resolved.", location()); } // calculate a path if transparent entities are needed in between. Rooted field = parent->getDescriptor()->getFieldDescriptors()[fieldIdx]; size_t lastFieldIdx = fieldIdx; auto pathRes = field->pathTo(strct, logger()); if (!pathRes.second) { if (scope().getLeaf().cast()->transparent) { // if we have transparent elements above us in the structure // tree we try to unwind them before we give up. // pop the implicit field. scope().pop(logger()); // pop the implicit element. scope().pop(logger()); continue; } throw LoggableException( std::string("An instance of \"") + strct->getName() + "\" is not allowed as child of field \"" + field->getName() + "\" of descriptor \"" + parent->getDescriptor()->getName() + "\"", location()); } if (!pathRes.first.empty()) { createPath(lastFieldIdx, pathRes.first, parent); lastFieldIdx = parent->getDescriptor()->getFieldDescriptorIndex(); } // create the entity for the new element at last. //TODO: REMOVE strct_name = strct->getName(); entity = parent->createChildStructuredEntity(strct, lastFieldIdx, args, nameAttr); } entity->setLocation(location()); scope().push(entity); return true; } } void DocumentChildHandler::end() { // in case of explicit fields we do not want to pop something from the // stack. if (isExplicitField) { return; } // pop the "main" element. scope().pop(logger()); } bool DocumentChildHandler::fieldStart(bool &isDefault, size_t fieldIdx) { if (isExplicitField) { // In case of explicit fields we do not want to create another field. isDefault = true; return fieldIdx == 0; } Rooted parentNode = scope().getLeaf(); assert(parentNode->isa(&RttiTypes::StructuredEntity) || parentNode->isa(&RttiTypes::AnnotationEntity)); size_t dummy; DocumentEntity *parent; preamble(parentNode, dummy, parent); NodeVector fields = parent->getDescriptor()->getFieldDescriptors(); if (isDefault) { fieldIdx = fields.size() - 1; } else { if (fieldIdx >= fields.size()) { return false; } isDefault = fieldIdx == fields.size() - 1; } // push the field on the stack. Rooted field{ new DocumentField(manager(), parentNode, fieldIdx, false)}; field->setLocation(location()); scope().push(field); return true; } void DocumentChildHandler::fieldEnd() { assert(scope().getLeaf()->isa(&RttiTypes::DocumentField)); // pop the field from the stack. scope().pop(logger()); // pop all remaining transparent elements. while (scope().getLeaf()->isa(&RttiTypes::StructuredEntity) && scope().getLeaf().cast()->isTransparent()) { // pop the transparent element. scope().pop(logger()); // pop the transparent field. scope().pop(logger()); } } bool DocumentChildHandler::annotationStart(const Variant &className, Variant::mapType &args) { // TODO: Implement return false; } bool DocumentChildHandler::annotationEnd(const Variant &className, const Variant &elementName) { // TODO: Implement return false; } bool DocumentChildHandler::convertData(Handle field, Variant &data, Logger &logger) { bool valid = true; Rooted type = field->getPrimitiveType(); // If the content is supposed to be of type string, we only need to check // for "magic" values -- otherwise just call the "parseGenericString" // function on the string data if (type->isa(&RttiTypes::StringType)) { const std::string &str = data.asString(); // TODO: Referencing constants with "." separator should also work if (Utils::isIdentifier(str)) { data.markAsMagic(); } } else { // Parse the string as generic string, assign the result auto res = VariantReader::parseGenericString( data.asString(), logger, data.getLocation().getSourceId(), data.getLocation().getStart()); data = res.second; } // Now try to resolve the value for the primitive type return valid && scope().resolveValue(data, type, logger); } bool DocumentChildHandler::data(Variant &data) { Rooted parentField = scope().getLeaf(); assert(parentField->isa(&RttiTypes::DocumentField)); size_t fieldIdx; DocumentEntity *parent; preamble(parentField, fieldIdx, parent); Rooted desc = parent->getDescriptor(); // Retrieve the actual FieldDescriptor Rooted field = desc->getFieldDescriptors()[fieldIdx]; // If it is a primitive field directly, try to parse the content. if (field->isPrimitive()) { // Add it as primitive content. if (!convertData(field, data, logger())) { return false; } parent->createChildDocumentPrimitive(data, fieldIdx); return true; } // Search through all permitted default fields of the parent class that // allow primitive content at this point and could be constructed via // transparent intermediate entities. NodeVector defaultFields = field->getDefaultFields(); // Try to parse the data using the type specified by the respective field. // If that does not work we proceed to the next possible field. std::vector forks; for (auto primitiveField : defaultFields) { // Then try to parse the content using the type specification. forks.emplace_back(logger().fork()); if (!convertData(primitiveField, data, forks.back())) { continue; } // The conversion worked, commit any possible warnings forks.back().commit(); // Construct the necessary path NodeVector path = field->pathTo(primitiveField, logger()); // TODO: Create methods with indices instead of names. createPath(fieldIdx, path, parent); // Then create the primitive element parent->createChildDocumentPrimitive(data); return true; } // No field was found that might take the data -- dump the error messages // from the loggers -- or, if there were no primitive fields, clearly state // this fact if (defaultFields.empty()) { logger().error("Got data, but structure \"" + name() + "\" does not have any primitive field", data); } else { logger().error("Could not read data with any of the possible fields:", data); size_t f = 0; for (auto field : defaultFields) { logger().note(std::string("Field ") + Utils::join(field->path(), ".") + std::string(":"), SourceLocation{}, MessageMode::NO_CONTEXT); forks[f].commit(); f++; } } return false; } namespace States { const State Document = StateBuilder() .parent(&None) .createdNodeType(&RttiTypes::Document) .elementHandler(DocumentHandler::create) .arguments({Argument::String("name", "")}); const State DocumentChild = StateBuilder() .parents({&Document, &DocumentChild}) .createdNodeTypes({&RttiTypes::StructureNode, &RttiTypes::AnnotationEntity, &RttiTypes::DocumentField}) .elementHandler(DocumentChildHandler::create); } } namespace RttiTypes { const Rtti DocumentField = RttiBuilder( "DocumentField").parent(&Node); } }