blob: 508ea264b83419bf2ca1a1d6fc7c46a0b8f0a854 [file] [log] [blame]
/*
* 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
*
* https://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.
*/
using System;
using System.Collections.Generic;
namespace Avro.IO.Parsing
{
/// <summary>
/// The class that generates a grammar suitable to parse Avro data in JSON
/// format.
/// </summary>
public class JsonGrammarGenerator : ValidatingGrammarGenerator
{
/// <summary>
/// Returns the non-terminal that is the start symbol for the grammar for the
/// grammar for the given schema <tt>schema</tt>.
/// </summary>
public override Symbol Generate(Schema schema)
{
return Symbol.NewRoot(Generate(schema, new Dictionary<LitS, Symbol>()));
}
/// <summary>
/// Returns the non-terminal that is the start symbol for grammar of the given
/// schema <tt>sc</tt>. If there is already an entry for the given schema in the
/// given map <tt>seen</tt> then that entry is returned. Otherwise a new symbol
/// is generated and an entry is inserted into the map.
/// </summary>
/// <param name="sc"> The schema for which the start symbol is required </param>
/// <param name="seen"> A map of schema to symbol mapping done so far. </param>
/// <returns> The start symbol for the schema </returns>
protected override Symbol Generate(Schema sc, IDictionary<LitS, Symbol> seen)
{
switch (sc.Tag)
{
case Schema.Type.Null:
case Schema.Type.Boolean:
case Schema.Type.Int:
case Schema.Type.Long:
case Schema.Type.Float:
case Schema.Type.Double:
case Schema.Type.String:
case Schema.Type.Bytes:
case Schema.Type.Fixed:
case Schema.Type.Union:
return base.Generate(sc, seen);
case Schema.Type.Enumeration:
return Symbol.NewSeq(new Symbol.EnumLabelsAction(((EnumSchema)sc).Symbols), Symbol.Enum);
case Schema.Type.Array:
return Symbol.NewSeq(
Symbol.NewRepeat(Symbol.ArrayEnd, Symbol.ItemEnd, Generate(((ArraySchema)sc).ItemSchema, seen)),
Symbol.ArrayStart);
case Schema.Type.Map:
return Symbol.NewSeq(
Symbol.NewRepeat(Symbol.MapEnd, Symbol.ItemEnd, Generate(((MapSchema)sc).ValueSchema, seen),
Symbol.MapKeyMarker, Symbol.String), Symbol.MapStart);
case Schema.Type.Record:
{
LitS wsc = new LitS(sc);
if (!seen.TryGetValue(wsc, out Symbol rresult))
{
Symbol[] production = new Symbol[((RecordSchema)sc).Fields.Count * 3 + 2];
rresult = Symbol.NewSeq(production);
seen[wsc] = rresult;
int i = production.Length;
int n = 0;
production[--i] = Symbol.RecordStart;
foreach (Field f in ((RecordSchema)sc).Fields)
{
production[--i] = new Symbol.FieldAdjustAction(n, f.Name, f.Aliases);
production[--i] = Generate(f.Schema, seen);
production[--i] = Symbol.FieldEnd;
n++;
}
production[i - 1] = Symbol.RecordEnd;
}
return rresult;
}
case Schema.Type.Logical:
return Generate((sc as LogicalSchema).BaseSchema, seen);
default:
throw new Exception("Unexpected schema type");
}
}
}
}