/** * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include #include "Compiler.hh" #include "Types.hh" #include "Schema.hh" #include "ValidSchema.hh" #include "Stream.hh" #include "json/JsonDom.hh" extern void yyparse(void *ctx); using std::string; using std::map; using std::vector; namespace rmf_avro { typedef map SymbolTable; using json::Entity; // #define DEBUG_VERBOSE static NodePtr makePrimitive(const std::string& t) { if (t == "null") { return NodePtr(new NodePrimitive(AVRO_NULL)); } else if (t == "boolean") { return NodePtr(new NodePrimitive(AVRO_BOOL)); } else if (t == "int") { return NodePtr(new NodePrimitive(AVRO_INT)); } else if (t == "long") { return NodePtr(new NodePrimitive(AVRO_LONG)); } else if (t == "float") { return NodePtr(new NodePrimitive(AVRO_FLOAT)); } else if (t == "double") { return NodePtr(new NodePrimitive(AVRO_DOUBLE)); } else if (t == "string") { return NodePtr(new NodePrimitive(AVRO_STRING)); } else if (t == "bytes") { return NodePtr(new NodePrimitive(AVRO_BYTES)); } else { return NodePtr(); } } static NodePtr makeNode(const json::Entity& e, SymbolTable& st, const string& ns); template concepts::SingleAttribute asSingleAttribute(const T& t) { concepts::SingleAttribute n; n.add(t); return n; } static bool isFullName(const string& s) { return s.find('.') != string::npos; } static Name getName(const string& name, const string& ns) { return (isFullName(name)) ? Name(name) : Name(name, ns); } static NodePtr makeNode(const std::string& t, SymbolTable& st, const string& ns) { NodePtr result = makePrimitive(t); if (result) { return result; } Name n = getName(t, ns); map::const_iterator it = st.find(n); if (it != st.end()) { return NodePtr(new NodeSymbolic(asSingleAttribute(n), it->second)); } throw Exception(boost::format("Unknown type: %1%") % n.fullname()); } const map::const_iterator findField(const Entity& e, const map& m, const string& fieldName) { map::const_iterator it = m.find(fieldName); if (it == m.end()) { throw Exception(boost::format("Missing Json field \"%1%\": %2%") % fieldName % e.toString()); } else { return it; } } template const T& getField(const Entity& e, const map& m, const string& fieldName) { map::const_iterator it = findField(e, m, fieldName); if (it->second.type() != json::type_traits::type()) { throw Exception(boost::format( "Json field \"%1%\" is not a %2%: %3%") % fieldName % json::type_traits::name() % it->second.toString()); } else { return it->second.value(); } } struct Field { const string& name; const NodePtr value; Field(const string& n, const NodePtr& v) : name(n), value(v) { } }; static Field makeField(const Entity& e, SymbolTable& st, const string& ns) { const map& m = e.value >(); const string& n = getField(e, m, "name"); map::const_iterator it = findField(e, m, "type"); return Field(n, makeNode(it->second, st, ns)); } static NodePtr makeRecordNode(const Entity& e, const Name& name, const map& m, SymbolTable& st, const string& ns) { const vector& v = getField >(e, m, "fields"); concepts::MultiAttribute fieldNames; concepts::MultiAttribute fieldValues; for (vector::const_iterator it = v.begin(); it != v.end(); ++it) { Field f = makeField(*it, st, ns); fieldNames.add(f.name); fieldValues.add(f.value); } return NodePtr(new NodeRecord(asSingleAttribute(name), fieldValues, fieldNames)); } static NodePtr makeEnumNode(const Entity& e, const Name& name, const map& m) { const vector& v = getField >(e, m, "symbols"); concepts::MultiAttribute symbols; for (vector::const_iterator it = v.begin(); it != v.end(); ++it) { if (it->type() != json::etString) { throw Exception(boost::format("Enum symbol not a string: %1%") % it->toString()); } symbols.add(it->value()); } return NodePtr(new NodeEnum(asSingleAttribute(name), symbols)); } static NodePtr makeFixedNode(const Entity& e, const Name& name, const map& m) { int v = static_cast(getField(e, m, "size")); if (v <= 0) { throw Exception(boost::format("Size for fixed is not positive: ") % e.toString()); } return NodePtr(new NodeFixed(asSingleAttribute(name), asSingleAttribute(v))); } static NodePtr makeArrayNode(const Entity& e, const map& m, SymbolTable& st, const string& ns) { map::const_iterator it = findField(e, m, "items"); return NodePtr(new NodeArray(asSingleAttribute( makeNode(it->second, st, ns)))); } static NodePtr makeMapNode(const Entity& e, const map& m, SymbolTable& st, const string& ns) { map::const_iterator it = findField(e, m, "values"); return NodePtr(new NodeMap(asSingleAttribute( makeNode(it->second, st, ns)))); } static Name getName(const Entity& e, const map& m, const string& ns) { const string& name = getField(e, m, "name"); if (isFullName(name)) { return Name(name); } else { map::const_iterator it = m.find("namespace"); if (it != m.end()) { if (it->second.type() != json::type_traits::type()) { throw Exception(boost::format( "Json field \"%1%\" is not a %2%: %3%") % "namespace" % json::type_traits::name() % it->second.toString()); } Name result = Name(name, it->second.value()); return result; } return Name(name, ns); } } static NodePtr makeNode(const Entity& e, const map& m, SymbolTable& st, const string& ns) { const string& type = getField(e, m, "type"); if (NodePtr result = makePrimitive(type)) { if (m.size() > 1) { throw Exception(boost::format( "Unknown additional Json fields: %1%") % e.toString()); } else { return result; } } else if (type == "record" || type == "error" || type == "enum" || type == "fixed") { Name nm = getName(e, m, ns); NodePtr result; if (type == "record" || type == "error") { result = NodePtr(new NodeRecord()); st[nm] = result; NodePtr r = makeRecordNode(e, nm, m, st, nm.ns()); (boost::dynamic_pointer_cast(r))->swap( *boost::dynamic_pointer_cast(result)); } else { result = (type == "enum") ? makeEnumNode(e, nm, m) : makeFixedNode(e, nm, m); st[nm] = result; } return result; } else if (type == "array") { return makeArrayNode(e, m, st, ns); } else if (type == "map") { return makeMapNode(e, m, st, ns); } throw Exception(boost::format("Unknown type definition: %1%") % e.toString()); } static NodePtr makeNode(const Entity& e, const vector& m, SymbolTable& st, const string& ns) { concepts::MultiAttribute mm; for (vector::const_iterator it = m.begin(); it != m.end(); ++it) { mm.add(makeNode(*it, st, ns)); } return NodePtr(new NodeUnion(mm)); } static NodePtr makeNode(const json::Entity& e, SymbolTable& st, const string& ns) { switch (e.type()) { case json::etString: return makeNode(e.value(), st, ns); case json::etObject: return makeNode(e, e.value >(), st, ns); case json::etArray: return makeNode(e, e.value >(), st, ns); default: throw Exception(boost::format("Invalid Avro type: %1%") % e.toString()); } } ValidSchema compileJsonSchemaFromStream(InputStream& is) { json::Entity e = json::loadEntity(is); SymbolTable st; NodePtr n = makeNode(e, st, ""); return ValidSchema(n); } ValidSchema compileJsonSchemaFromMemory(const uint8_t* input, size_t len) { return compileJsonSchemaFromStream(*memoryInputStream(input, len)); } ValidSchema compileJsonSchemaFromString(const char* input) { return compileJsonSchemaFromMemory(reinterpret_cast(input), ::strlen(input)); } static ValidSchema compile(std::istream& is) { std::auto_ptr in = istreamInputStream(is); return compileJsonSchemaFromStream(*in); } void compileJsonSchema(std::istream &is, ValidSchema &schema) { if (!is.good()) { throw Exception("Input stream is not good"); } schema = compile(is); } bool compileJsonSchema(std::istream &is, ValidSchema &schema, std::string &error) { try { compileJsonSchema(is, schema); return true; } catch (const Exception &e) { error = e.what(); return false; } } } // namespace rmf_avro