/// <summary> /// Creates a structurally equivalent member initialization from a given member initialization. /// </summary> /// <param name="memberInit">The member initialization to copy the structure of.</param> /// <returns>A structurally equivalent copy of the given member initialization.</returns> public static IStonMemberInit Copy(IStonMemberInit memberInit) { if (memberInit == null) { throw new ArgumentNullException("memberInit"); } return(new StonMemberInit(memberInit)); }
public AComplexEntity(IStonConstruction construction = null, IStonMemberInit memberInit = null, IStonCollectionInit collectionInit = null, IStonType type = null, string globalIdentifier = null) { GlobalIdentifier = globalIdentifier; Type = type; Construction = construction; MemberInit = memberInit; CollectionInit = collectionInit; }
// checks that a given combination of complex value components is valid private void ExpectValidComplexEntity(IStonConstruction construction = null, IStonMemberInit memberInit = null, IStonCollectionInit collectionInit = null) { try { new StonComplexEntity(construction, memberInit, collectionInit); } catch (StonException ex) { Assert.Fail(ex.Message); } }
/// <summary> /// Creates a new complex-valued entity, with a given value and optional declared type and global identifier. /// </summary> /// <param name="construction">The construction part of the complex value.</param> /// <param name="memberInit">The member initialization part of the complex value.</param> /// <param name="collectionInit">The collection initialization part of the complex value.</param> /// <param name="type">The declared type of the entity.</param> /// <param name="globalIdentifier">The global identifier of the entity.</param> public StonComplexEntity(IStonConstruction construction = null, IStonMemberInit memberInit = null, IStonCollectionInit collectionInit = null, IStonType type = null, string globalIdentifier = null) : base(type, globalIdentifier) { Construction = construction != null?StonConstruction.Copy(construction) : null; MemberInit = memberInit != null?StonMemberInit.Copy(memberInit) : null; CollectionInit = collectionInit != null?StonCollectionInit.Copy(collectionInit) : null; Helpers.Validator.ValidateEntity(this); }
/// <summary> /// Checks the validity of a given STON complex value member initialization. /// The validation does not include check for duplicate members, as that might require resolving references in a STON document. /// </summary> /// <param name="memberInit">The member initialization to check the validity of.</param> public static void ValidateMemberInit(IStonMemberInit memberInit) { if (memberInit == null) { throw new ArgumentNullException("memberInit"); } if (memberInit.MemberBindings == null) { throw new StonException("A complex value member initialization cannot have a non-existing member bindings collection."); } foreach (var memberBinding in memberInit.MemberBindings) { ValidateBindingKey(memberBinding.Key); if (memberBinding.Value == null) { throw new StonException("A complex value member initialization cannot have non-existing member values."); } } }
// writes a member initialization component of a complex value private void WriteMemberInit(StonTokenWriter writer, IStonMemberInit memberInit) { if (memberInit == null) { return; } if (memberInit.MemberBindings == null) { throw new StonException("A complex value member initialization cannot have a non-existing member bindings collection."); } writer.Write('{'); WriteSequence( writer, memberInit.MemberBindings, (w, mb) => { WriteBindingKey(w, mb.Key); w.Write(':'); WriteEntity(w, mb.Value); } ); writer.Write('}'); }
// checks that a given combination of complex value components is invalid, and causes a specific error private void ExpectInvalidComplexEntity(IStonConstruction construction = null, IStonMemberInit memberInit = null, IStonCollectionInit collectionInit = null, string message = null) { try { new StonComplexEntity(construction, memberInit, collectionInit); Assert.Fail("The value is valid. This should *not* have happened."); } catch (StonException ex) { Assert.AreEqual(message, ex.Message); } }
/// <summary> /// Creates a new complex-valued entity, with a given value and optional declared type and global identifier. /// </summary> /// <param name="construction">The construction part of the complex value.</param> /// <param name="memberInit">The member initialization part of the complex value.</param> /// <param name="collectionInit">The collection initialization part of the complex value.</param> /// <param name="type">The declared type of the entity.</param> /// <param name="globalIdentifier">The global identifier of the entity.</param> /// <returns>The new STON entity.</returns> public IStonComplexEntity CreateComplexEntity(IStonConstruction construction = null, IStonMemberInit memberInit = null, IStonCollectionInit collectionInit = null, IStonType type = null, string globalIdentifier = null) => new StonComplexEntity(construction, memberInit, collectionInit, type, globalIdentifier);
/// <summary> /// Creates a structurally equivalent member initialization from a given member initialization. /// </summary> /// <param name="memberInit">The member initialization to copy the structure of.</param> public StonMemberInit(IStonMemberInit memberInit) : this(memberInit.MemberBindings) { }
// reads a STON entity, complete or address private IStonEntity _ReadEntity(StonTokenReader reader, bool isAddress) { // stores a CANUN identifier or path for later use until its purpose is known string storedWord = null; // determining the global identifier, if any string globalIdentifier = null; // the identifier is explicitly prepended with a global identifier sigil if (reader.Peek().HasChartype(StonChartype.IdentifierSigil)) { if (isAddress) { throw reader.MakeUnexpectedCharacterException(StonChartype.None); // identifiers are not allowed for index parameter entities } reader.ReadAndSkip(); globalIdentifier = reader.ReadCanun(); reader.ExpectAndSkip(StonChartype.IdentifierAssign); } // the entity begins with a CANUN name that might or might not be a global identifier else if (reader.Peek().HasChartype(StonChartype.CanunBegin)) { storedWord = reader.ReadCanun(); // if and only if the CANUN name is followed by a global identifier assignment token // the name is to be used as a global identifier if (reader.Peek().HasChartype(StonChartype.IdentifierAssign)) { if (isAddress) { throw reader.MakeUnexpectedCharacterException(StonChartype.None); // once again, identifiers are not allowed for index parameter entities } reader.ReadAndSkip(); globalIdentifier = storedWord; storedWord = null; } } // finding the address, if any if (reader.Peek().HasChartype(StonChartype.AddressBegin)) { // the reference entity cannot have a type declaration if (storedWord != null) { throw reader.MakeUnexpectedCharacterException(StonChartype.None); } return(ElementFactory.CreateReferenceEntity(ReadAddress(reader), globalIdentifier)); } // determining the type, if any IStonType type = null; bool isTypeFromWord = false; bool collectionStarted = false; // a variable indicating whether during reading type a collection initialization has been started or not // when bare type definitions are involved, a collection initialization might appear like a collection type suffix // so it's impossible to tell the two apart before reading the initial sign // no word is stored; either there was no word in the first place, or there was a global identifier before if (storedWord == null) { // storing a CANUN path, which might be a type or a named value if (reader.Peek().HasChartype(StonChartype.CanunBegin)) { storedWord = ReadCanunPath(reader); } // reading a type if the following token is characteristic for types else if (reader.Peek().HasChartype(StonChartype.Extension | StonChartype.TypeOpen)) { type = ReadBareUnion(reader, null, ref collectionStarted); } } else { // if a CANUN name is stored, it might be a part of a longer CANUN path // which itself might be a type or a named value if (reader.Peek().HasChartype(StonChartype.NameSeparator)) { storedWord = storedWord + (char)reader.ReadAndSkip() + ReadCanunPath(reader); } } // if a CANUN path is followed by a type opening, collection suffix or a union type separator // it means that path must be a type name if (storedWord != null && reader.Peek().HasChartype(StonChartype.TypeOpen | StonChartype.CollectionSuffixBegin | StonChartype.UnionTypeSeparator)) { type = ReadBareUnion(reader, storedWord, ref collectionStarted); storedWord = null; } if (storedWord != null) { type = ElementFactory.CreateNamedType(storedWord); isTypeFromWord = true; } // finding the complex value, if any // when the collection initialization has been entered when reading a type definition // the rest of the collection initialization is read if (collectionStarted) { // a complex value is not allowed in an address index parameter if (isAddress) { throw reader.MakeUnexpectedCharacterException(StonChartype.CollectionSuffixContinue); } // reading the rest of the collection initialization // and subsequent member initialization, if available IStonCollectionInit collectionInit = ReadCollectionInit(reader, true); IStonMemberInit memberInit = null; if (reader.Peek().HasChartype(StonChartype.MemberInitOpen)) { memberInit = ReadMemberInit(reader); } return(ElementFactory.CreateComplexEntity(null, memberInit, collectionInit, type, globalIdentifier)); } // reading any valid combination of complex value components (construction, member initialization, collection initialization) if (reader.Peek().HasChartype(StonChartype.ConstructionOpen | StonChartype.MemberInitOpen | StonChartype.CollectionInitOpen)) { // a complex value is not allowed in an address index parameter if (isAddress) { throw reader.MakeUnexpectedCharacterException(StonChartype.None); } IStonConstruction constructor = null; IStonMemberInit memberInit = null; IStonCollectionInit collectionInit = null; if (reader.Peek().HasChartype(StonChartype.ConstructionOpen)) { constructor = ReadConstruction(reader); } if (reader.Peek().HasChartype(StonChartype.MemberInitOpen)) { memberInit = ReadMemberInit(reader); } if (reader.Peek().HasChartype(StonChartype.CollectionInitOpen)) { collectionInit = ReadCollectionInit(reader); if (memberInit == null && reader.Peek().HasChartype(StonChartype.MemberInitOpen)) { memberInit = ReadMemberInit(reader); } } return(ElementFactory.CreateComplexEntity(constructor, memberInit, collectionInit, type, globalIdentifier)); } // finding the string, binary or number simple value, if complex value is not present // reading a string literal or string literals chain if (reader.TryAndSkip(StonChartype.StringChainOpen)) { if (!reader.Peek().HasChartype(StonChartype.TextDelimiter | StonChartype.CodeDelimiter)) { throw reader.MakeUnexpectedCharacterException(StonChartype.TextDelimiter | StonChartype.CodeDelimiter); } } if (reader.Peek().HasChartype(StonChartype.TextDelimiter | StonChartype.CodeDelimiter)) { bool isCode = reader.Peek().HasChartype(StonChartype.CodeDelimiter); // whether the value is a text or a code var builder = new StringBuilder(); reader.ReadString(builder, isCode); while (reader.Peek().HasChartype(StonChartype.StringChainContinue)) { if (reader.ReadAndSkip().HasChartype(StonChartype.StringChainOpen)) { builder.Append('\n'); } reader.ReadString(builder, isCode); } return(ElementFactory.CreateSimpleEntity(ElementFactory.CreateSimpleValue(isCode ? StonDataType.Code : StonDataType.Text, builder.ToString()), type, globalIdentifier)); } // reading a number of binary value bool minus = false; // reading the initial sign, if any if (reader.Peek().HasChartype(StonChartype.Sign)) { if (reader.ReadAndSkip() == '-') { minus = true; } // a digit must follow the sign if (!reader.Peek().HasChartype(StonChartype.Digit)) { throw reader.MakeUnexpectedCharacterException(StonChartype.Digit); } } // both number and binary literals might begin with a zero (excluding initial sign) // whichever it is, is decided by whether the zero is followed by a base identifier or not if (reader.TryAndSkip(StonChartype.ZeroDigit)) { if (reader.Peek().HasChartype(StonChartype.BaseIdentifier)) { return(ElementFactory.CreateSimpleEntity(ElementFactory.CreateSimpleValue(StonDataType.Binary, reader.ReadBinaryContent(minus)), type, globalIdentifier)); } else { return(ElementFactory.CreateSimpleEntity(ElementFactory.CreateSimpleValue(StonDataType.Number, reader.ReadNumberContent(minus)), type, globalIdentifier)); } } // only number literals might begin with a non-zero digit (aside from initial sign) else if (reader.Peek().HasChartype(StonChartype.NonZeroDigit)) { return(ElementFactory.CreateSimpleEntity(ElementFactory.CreateSimpleValue(StonDataType.Number, reader.ReadNumberContent(minus)), type, globalIdentifier)); } // finding the named or null value, if everything else fails string value; if (reader.Peek().HasChartype(StonChartype.CanunBegin)) { value = ReadCanunPath(reader); } else if (isTypeFromWord) { value = (type as IStonNamedType).Name; type = null; } else { throw reader.MakeUnexpectedCharacterException(StonChartype.None); // there is no named value, and thus there's all the reason to panic } if (value == "null") { return(ElementFactory.CreateSimpleEntity(ElementFactory.CreateSimpleValue(StonDataType.Null, null), type, globalIdentifier)); } else { return(ElementFactory.CreateSimpleEntity(ElementFactory.CreateSimpleValue(StonDataType.Named, value), type, globalIdentifier)); } }