/* 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 "DocumentHandler.hpp" #include #include #include #include #include #include #include #include #include #include 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(); } /* DocumentChildHandler */ void DocumentChildHandler::preamble(Handle parentNode, std::string &fieldName, DocumentEntity *&parent, bool &inField) { // Check if the parent in the structure tree was an explicit field // reference. inField = parentNode->isa(&RttiTypes::DocumentField); if (inField) { fieldName = parentNode->getName(); parentNode = scope().selectOrThrow( {&RttiTypes::StructuredEntity, &RttiTypes::AnnotationEntity}); } else { // If it wasn't an explicit reference, we use the default field. fieldName = DEFAULT_FIELD_NAME; } // 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) { // TODO (@benjamin): These should be pushed onto the scope and poped once // the scope is left. Otherwise stuff may not be correclty resolved. size_t S = path.size(); for (size_t p = 1; p < S; p = p + 2) { parent = static_cast( parent->createChildStructuredEntity( path[p].cast(), Variant::mapType{}, path[p - 1]->getName(), "").get()); } } bool DocumentChildHandler::start(Variant::mapType &args) { scope().setFlag(ParserFlag::POST_HEAD, true); Rooted parentNode = scope().selectOrThrow( {&RttiTypes::Document, &RttiTypes::StructuredEntity, &RttiTypes::AnnotationEntity, &RttiTypes::DocumentField}); std::string fieldName; DocumentEntity *parent; bool inField; preamble(parentNode, fieldName, parent, inField); // 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. if (!inField && parent != nullptr && parent->getDescriptor()->hasField(name())) { Rooted field{ new DocumentField(parentNode->getManager(), fieldName, parentNode)}; field->setLocation(location()); scope().push(field); 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()); } std::string name; auto it = args.find("name"); if (it != args.end()) { name = it->second.asString(); args.erase(it); } Rooted entity; if (parentNode->isa(&RttiTypes::Document)) { entity = parentNode.cast()->createRootStructuredEntity( strct, args, name); } else { // calculate a path if transparent entities are needed in between. auto path = parent->getDescriptor()->pathTo(strct, logger()); if (path.empty()) { throw LoggableException( std::string("An instance of \"") + strct->getName() + "\" is not allowed as child of an instance of \"" + parent->getDescriptor()->getName() + "\"", location()); } // create all transparent entities until the last field. createPath(path, parent); entity = parent->createChildStructuredEntity(strct, args, fieldName, name); } entity->setLocation(location()); scope().push(entity); return true; } void DocumentChildHandler::end() { scope().pop(); } 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 parentNode = scope().selectOrThrow( {&RttiTypes::StructuredEntity, &RttiTypes::AnnotationEntity, &RttiTypes::DocumentField}); std::string fieldName; DocumentEntity *parent; bool inField; preamble(parentNode, fieldName, parent, inField); Rooted desc = parent->getDescriptor(); // We distinguish two cases here: One for fields that are given. if (fieldName != DEFAULT_FIELD_NAME) { // Retrieve the actual FieldDescriptor Rooted field = desc->getFieldDescriptor(fieldName); if (field == nullptr) { logger().error( std::string("Can't handle data because no field with name \"") + fieldName + "\" exists in descriptor\"" + desc->getName() + "\".", location()); return false; } // If it is not primitive at all, we can't parse the content. if (!field->isPrimitive()) { logger().error(std::string("Can't handle data because field \"") + fieldName + "\" of descriptor \"" + desc->getName() + "\" is not primitive!", location()); return false; } // Try to convert the data variable to the correct format, abort if this // does not work if (!convertData(field, data, logger())) { return false; } // Add it as primitive content parent->createChildDocumentPrimitive(data, fieldName); return true; } else { // The second case is for primitive fields. Here we search through // all FieldDescriptors that allow primitive content at this point // and could be constructed via transparent intermediate entities. // We then 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. NodeVector fields = desc->getDefaultFields(); std::vector forks; for (auto field : fields) { // Then try to parse the content using the type specification forks.emplace_back(logger().fork()); // Try to convert the data variable to the correct format, abort if // this does not work if (!convertData(field, data, forks.back())) { return false; } // Show possible warnings that were emitted by this type conversion forks.back().commit(); // If that worked, construct the necessary path auto pathRes = desc->pathTo(field, logger()); assert(pathRes.second); NodeVector path = pathRes.first; createPath(path, parent); // Then create the primitive element parent->createChildDocumentPrimitive(data, fieldName); return true; } logger().error("Could not read data with any of the possible fields:"); for (size_t f = 0; f < fields.size(); f++) { logger().note(Utils::join(fields[f]->path(), ".") + ":", SourceLocation{}, MessageMode::NO_CONTEXT); forks[f].commit(); } return false; } return true; } } namespace RttiTypes { const Rtti DocumentField = RttiBuilder( "DocumentField").parent(&Node); } }