private string _CreateSimpleArrayNode(string collectionName, bool appendNode) { Assert.IsNeitherNullNorEmpty(collectionName); Assert.AreEqual('[', collectionName[0]); var token = new PropertyNameInfo(collectionName); if (token.Type != PropertyNameTypes.SimpleExtensionCreationProperty) { throw new ArgumentException("The property name is improperly formatted for creating a new simple extension node."); } Assert.IsNeitherNullNorEmpty(token.Level1); Assert.IsNeitherNullNorEmpty(token.Level2); Assert.IsNeitherNullNorEmpty(token.SimpleExtensionNamespace); PropertyNode collectionNode = _EnsureSimpleCollection(token.SimpleExtensionNamespace, token.Level1); string xmlPrefix = collectionNode.ExtendedNamespacePrefix.Substring(1, collectionNode.ExtendedNamespacePrefix.Length - 2); XmlElement nodeElement = _document.CreateElement(xmlPrefix, token.Level2, collectionNode.ExtendedNamespace); // New nodes are set to nil. Version is implicitly "1". XmlAttribute elementIdAttribute = _namespaceManager.CreateElementIdAttribute(); elementIdAttribute.Value = Guid.NewGuid().ToString(); XmlAttribute typeAttribute = _namespaceManager.CreateNodeTypeAttribute(SchemaStrings.SchemaTypeArrayNode); nodeElement.Attributes.Append(elementIdAttribute); nodeElement.Attributes.Append(typeAttribute); _namespaceManager.AddNilAttribute(nodeElement); _namespaceManager.UpdateVersionAndModificationDate(nodeElement); PropertyNode newNode = null; // Now modifying the DOM (probably modified it by creating the collection earlier, too...). _SetUnusableOnException( () => { if (appendNode) { collectionNode.XmlNode.AppendChild(nodeElement); } else { collectionNode.XmlNode.PrependChild(nodeElement); } // ??? _contactTree.AdjustTree(); newNode = new PropertyNode(nodeElement, collectionNode, appendNode) {Version = 1}; _FixupChildren(collectionNode, token.Level2); }, null); _SuperExpensiveDeepValidate(this); Assert.IsNeitherNullNorEmpty(newNode.IContactName); return newNode.IContactName; }
/// <summary> /// Does the given string represent a legal array-node property name? /// </summary> /// <param name="propertyName">The string to check.</param> /// <returns>Returns whether the string is a legal array node or level-3 property..</returns> public static bool IsPropertyValidNode(string propertyName) { var pni = new PropertyNameInfo(propertyName); Assert.AreNotEqual(pni.Type, PropertyNameTypes.None); return pni.Type == PropertyNameTypes.SchematizedNode || pni.Type == PropertyNameTypes.SimpleExtensionNode; }
// TODO: Need to write the schema type on simple extensions. // TODO: Need to add valueType attribute for binary data. private void _SetString(string propertyName, string value, string valueType, string schemaType) { Assert.IsNeitherNullNorEmpty(propertyName); Assert.IsNeitherNullNorEmpty(value); Assert.IsNeitherNullNorEmpty(schemaType); Assert.Implies(schemaType == SchemaStrings.SchemaTypeBinary, !string.IsNullOrEmpty(valueType)); Assert.Implies(schemaType != SchemaStrings.SchemaTypeBinary, null == valueType); var token = new PropertyNameInfo(propertyName); PropertyNode parentProperty; string elementName; string nodeName; switch (token.Type) { case PropertyNameTypes.SchematizedCollectionName: case PropertyNameTypes.SimpleExtensionNode: case PropertyNameTypes.SchematizedNode: case PropertyNameTypes.SimpleExtensionCreationProperty: // None of these types of properties can have values set on them. throw new ArgumentException("This type of property cannot have a value set to it."); case PropertyNameTypes.SchematizedHierarchicalProperty: // If it's a hierarchical property, need to make sure that the parent node already exists. nodeName = token.Level1 + "/" + token.Level2 + "[" + token.Index + "]"; parentProperty = _contactTree.Lookup.FindNode(nodeName, NodeTypes.Any); if (null == parentProperty) { throw new PropertyNotFoundException("The node where the property is to be set doesn't exist. The node needs to be created first.", nodeName); } elementName = token.Level3; break; case PropertyNameTypes.SimpleExtensionHierarchicalProperty: // If it's a hierarchical property, need to make sure that the parent node already exists. nodeName = "[" + token.SimpleExtensionNamespace + "]" + token.Level1 + "/" + token.Level2 + "[" + token.Index + "]"; parentProperty = _contactTree.Lookup.FindNode(nodeName, NodeTypes.Any); if (null == parentProperty) { throw new PropertyNotFoundException("The node where the property is to be set doesn't exist. The node needs to be created first.", nodeName); } elementName = token.Level3; break; case PropertyNameTypes.SchematizedTopLevelProperty: // Nothing needs to be done for schematized top level properties. Parent is the root Contact node. parentProperty = _contactTree.ContactRoot; elementName = token.Level1; break; case PropertyNameTypes.SimpleExtensionTopLevel: // the root is just the extension root. parentProperty = _contactTree.ExtendedRoot; elementName = token.Level1; break; default: Assert.Fail(); throw new ArgumentException("Invalid property name.", "propertyName"); } Assert.IsNotNull(parentProperty); Assert.Implies(parentProperty.ContactNodeType != NodeTypes.ElementNode, parentProperty.ContactNodeType == NodeTypes.RootElement); // See if the node we're trying to write to already exists. // Otherwise we'll need to create it (and rollback its creation on failure). PropertyNode oldProperty = null; foreach (PropertyNode child in parentProperty.Children) { if (child.IContactName == propertyName) { oldProperty = child; // For simple extensions, make sure that if this is a top level property if (child.ContactNodeType == NodeTypes.ElementCollection) { throw new ArgumentException("This property represents a collection and doesn't support having values set on it."); } // I think with the current implementation the XML could have been explicitly manipulated to violate this. // Might need to make the conditions around this assert a bit more bullet-proof. Assert.AreEqual(0, child.Children.Count); break; } } XmlNode childNode; if (null == oldProperty) { if (string.IsNullOrEmpty(token.SimpleExtensionNamespace)) { childNode = _namespaceManager.CreateSchemaElement(elementName); } else { childNode = _namespaceManager.CreateExtensionElement(token.SimpleExtensionNamespace, elementName); } } else { childNode = oldProperty.XmlNode.CloneNode(false); XmlUtil.RemoveNilAttribute(childNode); } _namespaceManager.UpdateVersionAndModificationDate(childNode); XmlText valueText = _document.CreateTextNode(value); childNode.AppendChild(valueText); if (schemaType == SchemaStrings.SchemaTypeBinary) { _namespaceManager.SetMimeTypeAttribute(childNode, valueType); } if (!string.IsNullOrEmpty(token.SimpleExtensionNamespace)) { _namespaceManager.SetSchemaTypeAttribute(childNode, schemaType); } //bool wasParentNil = false; _SetUnusableOnException( () => { if (parentProperty.XsiNil) { //wasParentNil = true; XmlUtil.RemoveNilAttribute(parentProperty.XmlNode); parentProperty.XsiNil = false; } PropertyNode childProperty; if (null == oldProperty) { parentProperty.XmlNode.PrependChild(childNode); // will need to remove this from parentProperty if we fail. childProperty = new PropertyNode(childNode, parentProperty, false, value, propertyName); } else { parentProperty.XmlNode.ReplaceChild(childNode, oldProperty.XmlNode); //PropertyNode.RebuildSubtree(childNode, oldProperty); childProperty = oldProperty.CloneForSwap(false, false, childNode); childProperty.Value = value; parentProperty.ReplaceChild(childProperty, oldProperty); } _ValidateDom(); _contactTree.Lookup.Replace(childProperty); _SuperExpensiveDeepValidate(this); }, null); }
/// <summary> /// Tries to parse the index out of a property name that might represent an array node. /// </summary> /// <param name="propertyName">The array node property name that contains the index to parse.</param> /// <returns>The zero-based parsed index if this appears to be an array node. Otherwise returns -1.</returns> public static int GetIndexFromNode(string propertyName) { var pni = new PropertyNameInfo(propertyName); Assert.AreNotEqual(pni.Type, PropertyNameTypes.None); if (pni.Type != PropertyNameTypes.SchematizedNode && pni.Type != PropertyNameTypes.SimpleExtensionNode) { return -1; } Assert.IsNeitherNullNorEmpty(pni.Index); int i; // PropertyNameInfo should have blocked negative values here. if (!Int32.TryParse(pni.Index, out i)) { return -1; } Assert.IsTrue(i > 0); --i; return i; }