private void EmitTag(List <Token <MarkupTokenType> > output, DataName elementName, IDictionary <DataName, Token <ModelTokenType> > attributes, MarkupTokenType tagType) { if (this.pendingNewLine) { if (this.Settings.PrettyPrint) { this.depth++; this.EmitNewLine(output); } this.pendingNewLine = false; } PrefixScopeChain.Scope scope = new PrefixScopeChain.Scope(); scope.TagName = elementName; if (!this.ScopeChain.ContainsNamespace(elementName.NamespaceUri)) { scope[elementName.Prefix] = elementName.NamespaceUri; } this.ScopeChain.Push(scope); switch (tagType) { case MarkupTokenType.ElementVoid: { output.Add(MarkupGrammar.TokenElementVoid(elementName)); break; } case MarkupTokenType.ElementEnd: { output.Add(MarkupGrammar.TokenElementEnd); break; } default: case MarkupTokenType.ElementBegin: { output.Add(MarkupGrammar.TokenElementBegin(elementName)); break; } } if (attributes != null) { foreach (var attr in attributes) { output.Add(MarkupGrammar.TokenAttribute(attr.Key)); output.Add(attr.Value.ChangeType(MarkupTokenType.Primitive)); } attributes.Clear(); } }
private void WriteTag( TextWriter writer, MarkupTokenType type, DataName tagName, IDictionary <DataName, Token <MarkupTokenType> > attributes, PrefixScopeChain.Scope prefixDeclarations) { if (String.IsNullOrEmpty(tagName.LocalName)) { // represents a document fragment return; } string tagPrefix = this.ScopeChain.EnsurePrefix(tagName.Prefix, tagName.NamespaceUri) ?? tagName.Prefix; // "<" writer.Write(MarkupGrammar.OperatorElementBegin); if (type == MarkupTokenType.ElementEnd) { // "/" writer.Write(MarkupGrammar.OperatorElementClose); } if (!String.IsNullOrEmpty(tagPrefix)) { // "prefix:" this.WriteLocalName(writer, tagPrefix); writer.Write(MarkupGrammar.OperatorPrefixDelim); } // "local-name" this.WriteLocalName(writer, tagName.LocalName); // emit all namespaces first, sorted by prefix if (prefixDeclarations != null) { foreach (var declaration in prefixDeclarations) { this.WriteXmlns(writer, declaration.Key, declaration.Value); } } if (attributes != null) { foreach (var attribute in attributes) { // Not sure if this is correct: http://stackoverflow.com/questions/3312390 // "The namespace name for an unprefixed attribute name always has no value" // "The attribute value in a default namespace declaration MAY be empty. // This has the same effect, within the scope of the declaration, of there being no default namespace." // http://www.w3.org/TR/xml-names/#defaulting string attrPrefix = this.ScopeChain.EnsurePrefix(attribute.Key.Prefix, attribute.Key.NamespaceUri) ?? attribute.Key.Prefix; this.WriteAttribute(writer, attrPrefix, attribute.Key.LocalName, attribute.Value); } } if (!this.canonicalForm && type == MarkupTokenType.ElementVoid) { // " /" writer.Write(MarkupGrammar.OperatorValueDelim); writer.Write(MarkupGrammar.OperatorElementClose); } // ">" writer.Write(MarkupGrammar.OperatorElementEnd); if (this.canonicalForm && type == MarkupTokenType.ElementVoid) { // http://www.w3.org/TR/xml-c14n#Terminology this.WriteTag(writer, MarkupTokenType.ElementEnd, tagName, null, null); } }
/// <summary> /// Formats the token sequence to the writer /// </summary> /// <param name="writer"></param> /// <param name="tokens"></param> public void Format(IEnumerable <Token <MarkupTokenType> > tokens, TextWriter writer) { if (tokens == null) { throw new ArgumentNullException("tokens"); } IStream <Token <MarkupTokenType> > stream = Stream <Token <MarkupTokenType> > .Create(tokens); PrefixScopeChain.Scope scope = null; while (!stream.IsCompleted) { Token <MarkupTokenType> token = stream.Peek(); switch (token.TokenType) { case MarkupTokenType.ElementBegin: case MarkupTokenType.ElementVoid: { DataName tagName = token.Name; MarkupTokenType tagType = token.TokenType; stream.Pop(); token = stream.Peek(); scope = new PrefixScopeChain.Scope(); if (this.ScopeChain.ContainsNamespace(tagName.NamespaceUri) || (String.IsNullOrEmpty(tagName.NamespaceUri) && !this.ScopeChain.ContainsPrefix(String.Empty))) { string prefix = this.ScopeChain.GetPrefix(tagName.NamespaceUri, false); scope.TagName = new DataName(tagName.LocalName, prefix, tagName.NamespaceUri); } else { scope[tagName.Prefix] = tagName.NamespaceUri; scope.TagName = tagName; } this.ScopeChain.Push(scope); IDictionary <DataName, Token <MarkupTokenType> > attributes = null; while (!stream.IsCompleted && token.TokenType == MarkupTokenType.Attribute) { if (attributes == null) { attributes = this.canonicalForm ? (IDictionary <DataName, Token <MarkupTokenType> >) new CanonicalList() : (IDictionary <DataName, Token <MarkupTokenType> >) new Dictionary <DataName, Token <MarkupTokenType> >(); } DataName attrName = token.Name; string prefix = this.ScopeChain.EnsurePrefix(attrName.Prefix, attrName.NamespaceUri); if (prefix != null) { if (prefix != attrName.Prefix) { attrName = new DataName(attrName.LocalName, prefix, attrName.NamespaceUri, true); } if (!this.ScopeChain.ContainsNamespace(attrName.NamespaceUri) && (!String.IsNullOrEmpty(attrName.NamespaceUri) || this.ScopeChain.ContainsPrefix(String.Empty))) { scope[prefix] = attrName.NamespaceUri; } } stream.Pop(); token = stream.Peek(); attributes[attrName] = token ?? MarkupGrammar.TokenNone; stream.Pop(); token = stream.Peek(); } this.WriteTag(writer, tagType, tagName, attributes, scope); scope = null; break; } case MarkupTokenType.ElementEnd: { if (this.ScopeChain.HasScope) { this.WriteTag(writer, MarkupTokenType.ElementEnd, this.ScopeChain.Peek().TagName, null, null); this.ScopeChain.Pop(); } else { // TODO: decide if this is should throw an exception } stream.Pop(); token = stream.Peek(); break; } case MarkupTokenType.Primitive: { ITextFormattable <MarkupTokenType> formattable = token.Value as ITextFormattable <MarkupTokenType>; if (formattable != null) { formattable.Format(this, writer); } else { HtmlFormatter.HtmlEncode(writer, token.ValueAsString(), this.encodeNonAscii, this.canonicalForm); } stream.Pop(); token = stream.Peek(); break; } default: { throw new TokenException <MarkupTokenType>( token, String.Format(ErrorUnexpectedToken, token.TokenType)); } } } }
/// <summary> /// Consumes a sequence of tokens and produces a token sequence of a different type /// </summary> public IEnumerable <Token <ModelTokenType> > Transform(IEnumerable <Token <MarkupTokenType> > input) { if (input == null) { throw new ArgumentNullException("input"); } IStream <Token <MarkupTokenType> > stream = Stream <Token <MarkupTokenType> > .Create(input); PrefixScopeChain scopeChain = new PrefixScopeChain(); Token <MarkupTokenType> token = stream.Peek(); while (!stream.IsCompleted) { switch (token.TokenType) { case MarkupTokenType.ElementBegin: case MarkupTokenType.ElementVoid: { bool hasProperties = false; bool isVoid = (token.TokenType == MarkupTokenType.ElementVoid); DataName tagName = token.Name; yield return(ModelGrammar.TokenArrayBeginUnnamed); // NOTE: JSON doesn't support namespaces so resolve the name to prefix+':'+local-name yield return(ModelGrammar.TokenPrimitive(tagName.ToPrefixedName())); PrefixScopeChain.Scope scope = new PrefixScopeChain.Scope(); string prefix = scopeChain.GetPrefix(tagName.NamespaceUri, false); if (!StringComparer.Ordinal.Equals(prefix, tagName.Prefix) && !String.IsNullOrEmpty(tagName.NamespaceUri)) { scope[tagName.Prefix] = tagName.NamespaceUri; // new namespace scope so need to emit xmlns hasProperties = true; yield return(ModelGrammar.TokenObjectBeginUnnamed); } scope.TagName = tagName; scopeChain.Push(scope); stream.Pop(); token = stream.Peek(); while (!stream.IsCompleted && token.TokenType == MarkupTokenType.Attribute) { if (!hasProperties) { hasProperties = true; yield return(ModelGrammar.TokenObjectBeginUnnamed); } DataName attrName = token.Name; prefix = scopeChain.GetPrefix(attrName.NamespaceUri, false); if (!StringComparer.Ordinal.Equals(prefix, attrName.Prefix) && !String.IsNullOrEmpty(attrName.NamespaceUri)) { scope[attrName.Prefix] = attrName.NamespaceUri; } // NOTE: JSON doesn't support namespaces so resolve the name to prefix+':'+local-name yield return(ModelGrammar.TokenProperty(new DataName(attrName.ToPrefixedName()))); stream.Pop(); token = stream.Peek(); switch (token.TokenType) { case MarkupTokenType.Primitive: { yield return(token.ChangeType(ModelTokenType.Primitive)); break; } default: { throw new TokenException <MarkupTokenType>( token, String.Format(ErrorUnexpectedToken, token.TokenType)); } } stream.Pop(); token = stream.Peek(); } if (hasProperties) { foreach (var xmlns in scope) { if (String.IsNullOrEmpty(xmlns.Key)) { yield return(ModelGrammar.TokenProperty("xmlns")); } else { yield return(ModelGrammar.TokenProperty(String.Concat("xmlns:", xmlns.Key))); } yield return(ModelGrammar.TokenPrimitive(xmlns.Value)); } yield return(ModelGrammar.TokenObjectEnd); } if (isVoid) { yield return(ModelGrammar.TokenArrayEnd); scopeChain.Pop(); } break; } case MarkupTokenType.ElementEnd: { if (scopeChain.Count > 0) { yield return(ModelGrammar.TokenArrayEnd); } scopeChain.Pop(); stream.Pop(); token = stream.Peek(); break; } case MarkupTokenType.Primitive: { if (token.Value is ITextFormattable <ModelTokenType> || token.Value is ITextFormattable <MarkupTokenType> ) { yield return(token.ChangeType(ModelTokenType.Primitive)); stream.Pop(); token = stream.Peek(); break; } string value = token.ValueAsString(); stream.Pop(); token = stream.Peek(); while (!stream.IsCompleted && (token.TokenType == MarkupTokenType.Primitive) && !(token.Value is ITextFormattable <ModelTokenType>) && !(token.Value is ITextFormattable <MarkupTokenType>)) { // concatenate adjacent value nodes value = String.Concat(value, token.ValueAsString()); stream.Pop(); token = stream.Peek(); } switch (this.Whitespace) { case WhitespaceType.Normalize: { // replace whitespace chunks with single space (HTML-style normalization) value = JsonMLInTransformer.RegexWhitespace.Replace(value, JsonMLInTransformer.SingleSpace); goto default; } case WhitespaceType.None: { if (CharUtility.IsNullOrWhiteSpace(value)) { break; } goto default; } case WhitespaceType.Preserve: default: { yield return(ModelGrammar.TokenPrimitive(value)); break; } } break; } case MarkupTokenType.Attribute: default: { throw new TokenException <MarkupTokenType>( token, String.Format(ErrorUnexpectedToken, token.TokenType)); } } } while (scopeChain.Count > 0) { scopeChain.Pop(); yield return(ModelGrammar.TokenArrayEnd); } }