Merge the recent changes from HEAD onto the branch
[beagle.git] / Util / SemWeb / XmlWriter.cs
blobfb36cba7fd1de8aa27e4a1b39c2d28666d2bc673
1 using System;
2 using System.Collections;
3 using System.IO;
4 using System.Text;
5 using System.Xml;
7 using SemWeb;
9 namespace SemWeb {
10 public class RdfXmlWriter : RdfWriter {
11 XmlWriter writer;
12 NamespaceManager ns;
14 XmlDocument doc;
16 Hashtable nodeMap = new Hashtable();
18 long anonCounter = 0;
19 Hashtable anonAlloc = new Hashtable();
21 public RdfXmlWriter(string file) : this(file, null) { }
23 public RdfXmlWriter(string file, NamespaceManager ns) : this(GetWriter(file), ns) { }
25 public RdfXmlWriter(TextWriter writer) : this(writer, null) { }
27 public RdfXmlWriter(TextWriter writer, NamespaceManager ns) : this(NewWriter(writer), ns) { }
29 private static XmlWriter NewWriter(TextWriter writer) {
30 XmlTextWriter ret = new XmlTextWriter(writer);
31 ret.Formatting = Formatting.Indented;
32 ret.Indentation = 1;
33 ret.IndentChar = '\t';
34 ret.Namespaces = true;
35 return ret;
38 public RdfXmlWriter(XmlWriter writer) : this(writer, null) { }
40 public RdfXmlWriter(XmlWriter writer, NamespaceManager ns) {
41 if (ns == null)
42 ns = new NamespaceManager();
43 this.writer = writer;
44 this.ns = ns;
47 private void Start() {
48 if (doc != null) return;
49 doc = new XmlDocument();
51 string rdfprefix = ns.GetPrefix(NS.RDF);
52 if (rdfprefix == null) {
53 if (ns.GetNamespace("rdf") == null) {
54 rdfprefix = "rdf";
55 ns.AddNamespace(NS.RDF, "rdf");
59 XmlElement root = doc.CreateElement(rdfprefix + ":RDF", NS.RDF);
60 foreach (string prefix in ns.GetPrefixes())
61 root.SetAttribute("xmlns:" + prefix, ns.GetNamespace(prefix));
62 doc.AppendChild(root);
65 public override NamespaceManager Namespaces { get { return ns; } }
67 char[] normalizechars = { '#', '/' };
69 private void Normalize(string uri, out string prefix, out string localname) {
70 if (uri == "")
71 throw new InvalidOperationException("The empty URI cannot be used as an element node.");
73 if (ns.Normalize(uri, out prefix, out localname))
74 return;
76 // No namespace prefix was registered, so come up with something.
78 int last = uri.LastIndexOfAny(normalizechars);
79 if (last <= 0)
80 throw new InvalidOperationException("No namespace was registered and no prefix could be automatically generated for <" + uri + ">");
82 int prev = uri.LastIndexOfAny(normalizechars, last-1);
83 if (prev <= 0)
84 throw new InvalidOperationException("No namespace was registered and no prefix could be automatically generated for <" + uri + ">");
86 string n = uri.Substring(0, last+1);
87 localname = uri.Substring(last+1);
89 // TODO: Make sure the local name (here and anywhere in this
90 // class) is a valid XML name.
92 if (Namespaces.GetPrefix(n) != null) {
93 prefix = Namespaces.GetPrefix(n);
94 return;
97 prefix = uri.Substring(prev+1, last-prev-1);
99 // Remove all non-xmlable (letter) characters.
100 StringBuilder newprefix = new StringBuilder();
101 foreach (char c in prefix)
102 if (char.IsLetter(c))
103 newprefix.Append(c);
104 prefix = newprefix.ToString();
106 if (prefix.Length == 0) {
107 // There were no letters in the prefix!
108 prefix = "ns";
111 if (Namespaces.GetNamespace(prefix) == null) {
112 doc.DocumentElement.SetAttribute("xmlns:" + prefix, n);
113 Namespaces.AddNamespace(n, prefix);
114 return;
117 int ctr = 1;
118 while (true) {
119 if (Namespaces.GetNamespace(prefix + ctr) == null) {
120 prefix += ctr;
121 doc.DocumentElement.SetAttribute("xmlns:" + prefix, n);
122 Namespaces.AddNamespace(n, prefix);
123 return;
125 ctr++;
129 private void SetAttribute(XmlElement element, string nsuri, string prefix, string localname, string val) {
130 XmlAttribute attr = doc.CreateAttribute(prefix, localname, nsuri);
131 attr.InnerXml = val;
132 element.SetAttributeNode(attr);
135 private XmlElement GetNode(string uri, string type, XmlElement context) {
136 if (nodeMap.ContainsKey(uri))
137 return (XmlElement)nodeMap[uri];
139 Start();
141 XmlElement node;
142 if (type == null) {
143 node = doc.CreateElement(ns.GetPrefix(NS.RDF) + ":Description", NS.RDF);
144 } else {
145 string prefix, localname;
146 Normalize(type, out prefix, out localname);
147 node = doc.CreateElement(prefix + ":" + localname, ns.GetNamespace(prefix));
150 if (!anonAlloc.ContainsKey(uri)) {
151 SetAttribute(node, NS.RDF, ns.GetPrefix(NS.RDF), "about", uri);
152 } else {
153 SetAttribute(node, NS.RDF, ns.GetPrefix(NS.RDF), "nodeID", uri);
156 if (context == null)
157 doc.DocumentElement.AppendChild(node);
158 else
159 context.AppendChild(node);
161 nodeMap[uri] = node;
162 return node;
165 private XmlElement CreatePredicate(XmlElement subject, string predicate) {
166 string prefix, localname;
167 Normalize(predicate, out prefix, out localname);
168 XmlElement pred = doc.CreateElement(prefix + ":" + localname, ns.GetNamespace(prefix));
169 subject.AppendChild(pred);
170 return pred;
173 public override void WriteStatement(string subj, string pred, string obj) {
174 XmlElement subjnode = GetNode(subj, pred == "http://www.w3.org/1999/02/22-rdf-syntax-ns#type" ? obj : null, null);
175 if (pred == "http://www.w3.org/1999/02/22-rdf-syntax-ns#type") return;
177 XmlElement prednode = CreatePredicate(subjnode, pred);
178 if (nodeMap.ContainsKey(obj)) {
179 if (!anonAlloc.ContainsKey(obj)) {
180 SetAttribute(prednode, NS.RDF, ns.GetPrefix(NS.RDF), "resource", obj);
181 } else {
182 SetAttribute(prednode, NS.RDF, ns.GetPrefix(NS.RDF), "nodeID", obj);
184 } else {
185 GetNode(obj, null, prednode);
189 public override void WriteStatement(string subj, string pred, Literal literal) {
190 XmlElement subjnode = GetNode(subj, null, null);
191 XmlElement prednode = CreatePredicate(subjnode, pred);
192 prednode.InnerText = literal.Value;
195 public override string CreateAnonymousEntity() {
196 string id = "anon" + (anonCounter++);
197 anonAlloc[id] = anonAlloc;
198 return id;
201 public override void Close() {
202 base.Close();
203 if (doc != null)
204 doc.WriteTo(writer);
205 //writer.Close();
209 internal class RdfXmlWriter2 : RdfWriter {
210 XmlWriter writer;
211 NamespaceManager ns;
212 NamespaceManager autons;
214 long anonCounter = 0;
215 string currentSubject = null;
217 string rdf;
219 public RdfXmlWriter2(string file) : this(file, null) { }
221 public RdfXmlWriter2(string file, NamespaceManager ns) : this(GetWriter(file), ns) { }
223 public RdfXmlWriter2(TextWriter writer) : this(writer, null) { }
225 public RdfXmlWriter2(TextWriter writer, NamespaceManager ns) : this(NewWriter(writer), ns) { }
227 private static XmlWriter NewWriter(TextWriter writer) {
228 XmlTextWriter ret = new XmlTextWriter(writer);
229 ret.Formatting = Formatting.Indented;
230 ret.Indentation = 1;
231 ret.IndentChar = '\t';
232 return ret;
235 public RdfXmlWriter2(XmlWriter writer) : this(writer, null) { }
237 public RdfXmlWriter2(XmlWriter writer, NamespaceManager ns) {
238 if (ns == null)
239 ns = new NamespaceManager();
240 this.writer = writer;
241 this.ns = ns;
242 //autons = new AutoPrefixNamespaceManager(this.ns);
245 public override NamespaceManager Namespaces { get { return ns; } }
247 private bool Open(string resource, string type) {
248 bool emittedType = false;
250 if (currentSubject == null) {
251 rdf = autons.GetPrefix("http://www.w3.org/1999/02/22-rdf-syntax-ns#");
252 writer.WriteStartElement(rdf + ":RDF");
253 foreach (string prefix in autons.GetPrefixes())
254 writer.WriteAttributeString("xmlns:" + prefix, autons.GetNamespace(prefix));
256 if (currentSubject != null && currentSubject != resource)
257 writer.WriteEndElement();
258 if (currentSubject == null || currentSubject != resource) {
259 currentSubject = resource;
261 if (type == null)
262 writer.WriteStartElement(rdf + ":Description");
263 else {
264 writer.WriteStartElement(URI(type));
265 emittedType = true;
268 writer.WriteAttributeString(rdf + ":about", resource);
271 return emittedType;
274 public override void WriteStatement(string subj, string pred, string obj) {
275 if (Open(subj, pred == "http://www.w3.org/1999/02/22-rdf-syntax-ns#type" ? obj : null))
276 return;
277 writer.WriteStartElement(URI(pred));
278 writer.WriteAttributeString("rdf:resource", obj);
279 writer.WriteEndElement();
282 public override void WriteStatement(string subj, string pred, Literal literal) {
283 Open(subj, null);
284 writer.WriteStartElement(URI(pred));
285 // Should write language and datatype
286 writer.WriteString(literal.Value);
287 writer.WriteEndElement();
290 public override string CreateAnonymousEntity() {
291 return "_:anon" + (anonCounter++);
294 public override void Close() {
295 base.Close();
296 if (currentSubject != null) {
297 writer.WriteEndElement();
298 writer.WriteEndElement();
300 //writer.Close();
304 private string URI(string uri) {
305 if (uri.StartsWith("_:anon")) return uri;
306 string ret = autons.Normalize(uri);
307 if (ret.StartsWith("<"))
308 throw new InvalidOperationException("A namespace prefix must be defined for the URI " + ret + ".");
309 return ret;