summaryrefslogtreecommitdiff
path: root/libxsd-frontend/xsd-frontend/transformations/schema-per-type.cxx
diff options
context:
space:
mode:
Diffstat (limited to 'libxsd-frontend/xsd-frontend/transformations/schema-per-type.cxx')
-rw-r--r--libxsd-frontend/xsd-frontend/transformations/schema-per-type.cxx453
1 files changed, 453 insertions, 0 deletions
diff --git a/libxsd-frontend/xsd-frontend/transformations/schema-per-type.cxx b/libxsd-frontend/xsd-frontend/transformations/schema-per-type.cxx
new file mode 100644
index 0000000..9ac8445
--- /dev/null
+++ b/libxsd-frontend/xsd-frontend/transformations/schema-per-type.cxx
@@ -0,0 +1,453 @@
+// file : xsd-frontend/transformations/schema-per-type.cxx
+// author : Boris Kolpackov <boris@codesynthesis.com>
+// copyright : Copyright (c) 2006-2010 Code Synthesis Tools CC
+// license : GNU GPL v2 + exceptions; see accompanying LICENSE file
+
+#include <xsd-frontend/transformations/schema-per-type.hxx>
+
+#include <xsd-frontend/semantic-graph.hxx>
+#include <xsd-frontend/traversal.hxx>
+
+#include <cult/containers/map.hxx>
+#include <cult/containers/set.hxx>
+#include <cult/containers/vector.hxx>
+
+#include <sstream>
+#include <iostream>
+
+#include <strings.h> // strcasecmp
+
+using std::wcerr;
+using std::endl;
+
+namespace XSDFrontend
+{
+ using namespace Cult;
+
+ typedef WideString String;
+ typedef Transformations::SchemaPerType::Failed Failed;
+
+ typedef Containers::Vector<SemanticGraph::Schema*> Schemas;
+ typedef Containers::Map<SemanticGraph::Type*,
+ SemanticGraph::Schema*> TypeSchemaMap;
+
+ // Compare file paths case-insensitively.
+ //
+ struct FileComparator
+ {
+ Boolean
+ operator() (NarrowString const& x, NarrowString const& y) const
+ {
+ return strcasecmp (x.c_str (), y.c_str ()) < 0;
+ }
+ };
+
+ typedef Containers::Set<NarrowString, FileComparator> FileSet;
+
+ namespace
+ {
+ // Go into included and imported schemas while making sure
+ // we don't process the same stuff more than once.
+ //
+ struct Uses: Traversal::Includes,
+ Traversal::Imports,
+ Traversal::Implies
+ {
+ Uses (Schemas& schemas, SemanticGraph::Schema*& xsd)
+ : schemas_ (schemas), xsd_ (xsd)
+ {
+ xsd_ = 0;
+ }
+
+ virtual Void
+ traverse (SemanticGraph::Includes& i)
+ {
+ SemanticGraph::Schema& s (i.schema ());
+
+ if (!s.context ().count ("xsd-frontend-schema-per-type-seen"))
+ {
+ schemas_.push_back (&s);
+ s.context ().set ("xsd-frontend-schema-per-type-seen", true);
+ Traversal::Includes::traverse (i);
+ }
+ }
+
+ virtual Void
+ traverse (SemanticGraph::Imports& i)
+ {
+ SemanticGraph::Schema& s (i.schema ());
+
+ if (!s.context ().count ("xsd-frontend-schema-per-type-seen"))
+ {
+ schemas_.push_back (&s);
+ s.context ().set ("xsd-frontend-schema-per-type-seen", true);
+ Traversal::Imports::traverse (i);
+ }
+ }
+
+ virtual Void
+ traverse (SemanticGraph::Implies& i)
+ {
+ if (xsd_ == 0)
+ xsd_ = &i.schema ();
+ }
+
+ private:
+ Schemas& schemas_;
+ SemanticGraph::Schema*& xsd_;
+ };
+
+ Void
+ process_schema (SemanticGraph::Schema& s,
+ SemanticGraph::Schema& root,
+ SemanticGraph::Schema& xsd,
+ TypeSchemaMap& tsm,
+ FileSet& file_set,
+ Transformations::SchemaPerTypeTranslator& trans)
+ {
+ using namespace SemanticGraph;
+
+ Path xsd_path ("XMLSchema.xsd");
+ Namespace& ns (dynamic_cast<Namespace&> (s.names_begin ()->named ()));
+
+ // We should be careful with iterator caching since we are going to
+ // remove some of the nodes.
+ //
+ for (Scope::NamesIterator i (ns.names_begin ()); i != ns.names_end ();)
+ {
+ Nameable& n (i->named ());
+
+ if (n.is_a<Type> ())
+ {
+ String name (n.name ());
+
+ // Remove from the namespace.
+ //
+ Scope::NamesIterator tmp (i++);
+ root.delete_edge (ns, n, *tmp);
+
+ // Add a new schema node.
+ //
+ Path path;
+ String tn (trans.translate_type (ns.name (), name));
+ String wbase (tn ? tn : name);
+
+ try
+ {
+ NarrowString base (wbase.to_narrow ());
+
+ // Escape directory separators unless they came from the
+ // translator.
+ //
+ if (!tn)
+ {
+ for (NarrowString::Iterator i (base.begin ()), e (base.end ());
+ i != e; ++i)
+ {
+ if (*i == '/' || *i == '\\')
+ *i = '_';
+ }
+ }
+
+ // Make sure it is unique.
+ //
+ NarrowString file_name (base);
+
+ for (UnsignedLong i (1);
+ file_set.find (file_name) != file_set.end ();
+ ++i)
+ {
+ std::ostringstream os;
+ os << i;
+ file_name = base + os.str ();
+ }
+
+ file_set.insert (file_name);
+ file_name += ".xsd";
+
+ try
+ {
+#if !defined(BOOST_FILESYSTEM_VERSION) || BOOST_FILESYSTEM_VERSION == 2
+ path = Path (file_name);
+#else
+ path = Path (file_name.c_str());
+#endif
+ }
+ catch (InvalidPath const&)
+ {
+ wcerr << "error: '" << file_name.c_str () << "' is not a valid "
+ << "filesystem path" << endl;
+
+ wcerr << "info: use type to file name translation mechanism "
+ << "to resolve this" << endl;
+
+ throw Failed ();
+ }
+ }
+ catch (String::NonRepresentable const&)
+ {
+ wcerr << "error: '" << wbase << "' cannot be represented as a "
+ << "narrow string" << endl;
+
+ wcerr << "info: use type to file name translation mechanism "
+ << "to resolve this" << endl;
+
+ throw Failed ();
+ }
+
+ Schema& ts (root.new_node<Schema> (path, 1, 1));
+ root.new_edge<Implies> (ts, xsd, xsd_path);
+
+ Namespace& tns (root.new_node<Namespace> (path, 1, 1));
+ root.new_edge<Names> (ts, tns, ns.name ());
+ root.new_edge<Names> (tns, n, name);
+
+ // Add include to the original schema and enter into the
+ // type-schema map.
+ //
+ root.new_edge<Includes> (s, ts, path);
+ tsm[&dynamic_cast<Type&> (n)] = &ts;
+ }
+ else
+ ++i;
+ }
+ }
+
+ struct Type: Traversal::List,
+ Traversal::Complex,
+ Traversal::Member
+ {
+ Type (SemanticGraph::Schema& schema,
+ SemanticGraph::Schema& root,
+ Char const* by_value_key,
+ TypeSchemaMap& tsm)
+ : schema_ (schema),
+ root_ (root),
+ by_value_key_ (by_value_key),
+ tsm_ (tsm)
+ {
+ *this >> names_ >> *this;
+ }
+
+ virtual Void
+ traverse (SemanticGraph::List& l)
+ {
+ // Treat item type as base type since it is impossible
+ // to create recursive constructs using list.
+ //
+ SemanticGraph::Type& t (l.argumented ().type ());
+ set_dep (t, false);
+ }
+
+ virtual Void
+ traverse (SemanticGraph::Complex& c)
+ {
+ if (c.inherits_p ())
+ set_dep (c.inherits ().base (), false);
+
+ Traversal::Complex::names (c);
+ }
+
+ virtual Void
+ traverse (SemanticGraph::Member& m)
+ {
+ SemanticGraph::Type& t (m.type ());
+
+ Boolean weak (
+ by_value_key_ == 0 ||
+ !t.context ().count (by_value_key_) ||
+ !t.context ().get<Boolean> (by_value_key_));
+
+ set_dep (t, weak);
+ }
+
+ private:
+ Void
+ set_dep (SemanticGraph::Type& t, Boolean weak)
+ {
+ using namespace SemanticGraph;
+
+ TypeSchemaMap::Iterator i (tsm_.find (&t));
+
+ // If a type is not present in the map then it must be
+ // a built-in type.
+ //
+ if (i == tsm_.end ())
+ return;
+
+ // Check if we already saw this type. Theoretically, it could
+ // be that we need to upgrade the type of include from weak to
+ // strong. But because inheritance is handled first, the type
+ // in the set will already be with the right type.
+ //
+ if (type_set_.find (&t) != type_set_.end ())
+ return;
+
+ type_set_.insert (&t);
+
+ Schema& s (*i->second);
+ Path path (s.used_begin ()->path ());
+ SemanticGraph::Uses* u;
+
+ if (s.names_begin ()->name () == schema_.names_begin ()->name ())
+ u = &root_.new_edge<Includes> (schema_, s, path);
+ else
+ u = &root_.new_edge<Imports> (schema_, s, path);
+
+ if (weak)
+ u->context().set ("weak", true);
+ }
+
+ private:
+ SemanticGraph::Schema& schema_;
+ SemanticGraph::Schema& root_;
+ Char const* by_value_key_;
+ TypeSchemaMap& tsm_;
+ Containers::Set<SemanticGraph::Type*> type_set_;
+
+ Traversal::Names names_;
+ };
+ }
+
+ namespace Transformations
+ {
+ SchemaPerType::
+ SchemaPerType (SchemaPerTypeTranslator& trans, Char const* by_value_key)
+ : by_value_key_ (by_value_key), trans_ (trans)
+ {
+ }
+
+ Schemas SchemaPerType::
+ transform (SemanticGraph::Schema& root)
+ {
+ // Collect initial schema nodes.
+ //
+ Schemas schemas;
+ SemanticGraph::Schema* xsd;
+
+ {
+ Traversal::Schema schema;
+ Uses uses (schemas, xsd);
+
+ schema >> uses >> schema;
+
+ // Some twisted schemas do recusive inclusions.
+ //
+ root.context ().set ("xsd-frontend-schema-per-type-seen", true);
+
+ schema.dispatch (root);
+ }
+
+ // wcerr << schemas.size () << " initial schema nodes" << endl;
+
+ // Add the schema file names to the file set.
+ //
+ FileSet file_set;
+
+ for (Schemas::Iterator i (schemas.begin ()); i != schemas.end (); ++i)
+ {
+ SemanticGraph::Path const& path (
+ (*i)->context ().get<SemanticGraph::Path> ("absolute-path"));
+
+ // Translate the schema file name.
+ //
+ NarrowString abs_path;
+
+#if !defined(BOOST_FILESYSTEM_VERSION) || BOOST_FILESYSTEM_VERSION == 2
+ // Try to use the portable representation of the path. If that
+ // fails, fall back to the native representation.
+ //
+ try
+ {
+ abs_path = path.string ();
+ }
+ catch (SemanticGraph::InvalidPath const&)
+ {
+ abs_path = path.native_file_string ();
+ }
+#else
+ // The new ABI does not have a fallback native representation
+ abs_path = path.string ();
+#endif
+
+ NarrowString tf (trans_.translate_schema (abs_path));
+#if !defined(BOOST_FILESYSTEM_VERSION) || BOOST_FILESYSTEM_VERSION == 2
+ NarrowString file (tf ? tf : path.leaf ());
+#else
+ NarrowString file (tf ? tf : path.filename ().string());
+#endif
+
+ Size p (file.rfind ('.'));
+ NarrowString ext (
+ p != NarrowString::npos ? NarrowString (file, p) : "");
+
+ NarrowString base (
+ p != NarrowString::npos ? NarrowString (file, 0, p) : file);
+
+ // Make sure it is unique.
+ //
+ NarrowString new_name (base);
+
+ for (UnsignedLong n (1);
+ file_set.find (new_name) != file_set.end ();
+ ++n)
+ {
+ std::ostringstream os;
+ os << n;
+ new_name = base + os.str ();
+ }
+
+ file_set.insert (new_name);
+ new_name += ext;
+
+ try
+ {
+#if !defined(BOOST_FILESYSTEM_VERSION) || BOOST_FILESYSTEM_VERSION == 2
+ (*i)->context ().set ("renamed", SemanticGraph::Path (new_name));
+#else
+ (*i)->context ().set ("renamed", SemanticGraph::Path (new_name.c_str()));
+#endif
+ }
+ catch (SemanticGraph::InvalidPath const&)
+ {
+ wcerr << "error: '" << new_name.c_str () << "' is not a valid "
+ << "filesystem path" << endl;
+
+ wcerr << "info: use schema file name translation mechanism "
+ << "to resolve this" << endl;
+
+ throw Failed ();
+ }
+ }
+
+ // Process each schema node.
+ //
+ TypeSchemaMap tsm;
+
+ for (Schemas::Iterator i (schemas.begin ()); i != schemas.end (); ++i)
+ {
+ process_schema (**i, root, *xsd, tsm, file_set, trans_);
+ }
+
+ // wcerr << tsm.size () << " type schema nodes" << endl;
+
+ // Establish include/import dependencies. While at it add the
+ // new schemas to the list which we will return.
+ //
+ for (TypeSchemaMap::Iterator i (tsm.begin ()); i != tsm.end (); ++i)
+ {
+ SemanticGraph::Schema& s (*i->second);
+ Type t (s, root, by_value_key_, tsm);
+ t.dispatch (*i->first);
+ schemas.push_back (&s);
+ }
+
+ return schemas;
+ }
+
+ SchemaPerTypeTranslator::
+ ~SchemaPerTypeTranslator ()
+ {
+ }
+ }
+}