/// <summary> /// Clears up any temporary files used with the creation of an object. /// </summary> /// <param name="sourceObjectFiles">The files to clear up.</param> private void ClearTemporaryFiles(SourceObjectFiles sourceObjectFiles) { // Sanity. if (null == sourceObjectFiles) { return; // No point throwing; nothing to clear up. } // Iterate over the files and clear them up. foreach (var sourceObjectFile in sourceObjectFiles.Cast <SourceObjectFile>()) { try { System.IO.File.Delete(sourceObjectFile.SourceFilePath); } catch (Exception e) { // TODO: Swallowing exceptions isn't nice. } } }
/// <summary> /// Clears up any temporary files used with the creation of an object. /// </summary> /// <param name="sourceObjectFiles">The files to clear up.</param> private void ClearTemporaryFiles(SourceObjectFiles sourceObjectFiles) { // Sanity. if (null == sourceObjectFiles) { return; // No point throwing; nothing to clear up. } // Iterate over the files and clear them up. foreach (var sourceObjectFile in sourceObjectFiles.Cast <SourceObjectFile>()) { try { System.IO.File.Delete(sourceObjectFile.SourceFilePath); } catch (Exception e) { SysUtils.ReportErrorToEventLog(SysUtils.DefaultEventSourceIdentifier, $"Exception removing temporary file from {sourceObjectFile.SourceFilePath}.", e); } } }
/// <summary> /// Executes the <see cref="ObjectSelector"/> rule against the <see cref="XNode"/>, /// importing matching objects. /// </summary> /// <param name="vault">The vault reference to use for processing the import.</param> /// <param name="node">The node to import data from.</param> /// <param name="objectSelector">The selector to execute.</param> /// <param name="xmlFile">The information about the XML file being imported.</param> /// <param name="parent">A parent object to create a relationship to, if appropriate.</param> /// <param name="xmlNamespaceManager">A namespace manager for using XML prefixes in XPath statements.</param> /// <returns>A list of files which were attached to the object, for deletion.</returns> public List <FileInfo> ImportXmlFile( Vault vault, XNode node, ObjectSelector objectSelector, FileInfo xmlFile = null, ObjVer parent = null, XmlNamespaceManager xmlNamespaceManager = null) { // Sanity. if (vault == null) { throw new ArgumentNullException(nameof(vault)); } if (null == node) { throw new ArgumentNullException(nameof(node)); } if (null == objectSelector) { throw new ArgumentNullException(nameof(objectSelector)); } if (string.IsNullOrWhiteSpace(objectSelector.XPathQuery)) { throw new ArgumentException("The XPath query for the object was empty", nameof(objectSelector)); } if (null == objectSelector.PropertySelectors) { throw new ArgumentException("The object selector contained no property selectors.", nameof(objectSelector)); } if (false == objectSelector.ObjectType.IsResolved) { throw new InvalidOperationException("The object selector object type is not resolved"); } if (false == objectSelector.Class.IsResolved) { throw new InvalidOperationException("The object selector class is not resolved"); } // Create a list of attached files (which can then be deleted later). var attachedFilesToDelete = new List <FileInfo>(); // Create the namespace manager. if (null != xmlNamespaceManager) { // Copy data from the other manager (so we don't accidentally affect other queries). var xmlNamespaceManager2 = new XmlNamespaceManager(new NameTable()); foreach (string prefix in xmlNamespaceManager) { // Don't add default. if (string.IsNullOrWhiteSpace(prefix)) { continue; } if (prefix == "xsi") { continue; } if (prefix == "xmlns") { continue; } // Add. xmlNamespaceManager2.AddNamespace(prefix, xmlNamespaceManager.LookupNamespace(prefix)); } xmlNamespaceManager = xmlNamespaceManager2; } else { xmlNamespaceManager = new XmlNamespaceManager(new NameTable()); } // Populate the namespace manager. if (null != objectSelector.XmlNamespaces) { foreach (var ns in objectSelector.XmlNamespaces) { // If the namespace manager already contains a prefix then remove it. string existingPrefix = xmlNamespaceManager.LookupPrefix(ns.Uri); if (false == string.IsNullOrEmpty(existingPrefix)) { xmlNamespaceManager.RemoveNamespace(existingPrefix, ns.Uri); } xmlNamespaceManager.AddNamespace(ns.Prefix, ns.Uri); } } // Find matching nodes. foreach (var matchingElement in node.XPathSelectElements(objectSelector.XPathQuery, xmlNamespaceManager)) { // Hold all the properties being read. var propertyValuesBuilder = new MFPropertyValuesBuilder(vault); // Add the class property value. propertyValuesBuilder.SetClass(objectSelector.Class.ID); // Retrieve the properties. foreach (var propertySelector in objectSelector.PropertySelectors) { // Sanity. if (string.IsNullOrWhiteSpace(propertySelector.XPathQuery)) { throw new ArgumentException("The object selector contained no property selectors.", nameof(objectSelector)); } if (false == propertySelector.PropertyDef.IsResolved) { throw new InvalidOperationException("The property value selector property definition is not resolved"); } // Retrieve the element for the property value. // var matchingPropertyElement = matchingElement // .XPathSelectElement(propertySelector.XPathQuery, xmlNamespaceManager); //if (null == matchingPropertyElement) // continue; // Find the property definition type. var propertyDefType = vault .PropertyDefOperations .GetPropertyDef(propertySelector.PropertyDef.ID) .DataType; // Check if it's lookup or multilookup var isLookup = ((propertyDefType == MFDataType.MFDatatypeMultiSelectLookup) || (propertyDefType == MFDataType.MFDatatypeLookup)); #region itterate XAttributes from XPath if (propertySelector.XPathQuery.Contains("@")) { List <int> listLookup = new List <int>(); IEnumerable matchingPropertyAttributes = (IEnumerable)matchingElement.XPathEvaluate(propertySelector.XPathQuery); foreach (System.Xml.Linq.XAttribute matchingPropertyAttribute in matchingPropertyAttributes) { string szValue = matchingPropertyAttribute.Value; if (propertyDefType == MFDataType.MFDatatypeBoolean) { propertyValuesBuilder.Add( propertySelector.PropertyDef.ID, propertyDefType, CastToBool(szValue)); } else if (propertyDefType == MFDataType.MFDatatypeDate) { szValue = $"{szValue} 00:00:00"; propertyValuesBuilder.Add( propertySelector.PropertyDef.ID, propertyDefType, szValue); } else if (isLookup) { var iLookupDef = (propertySelector.LookupOrValuelistStrategy == LookupOrValuelistStrategy.SearchLookup ? propertySelector.LookupObjectDef.ID : propertySelector.LookupValueListDef.ID); var iLookupItem = LookupRef(vault, iLookupDef, propertyDefType, propertySelector.SearchByLookupID, szValue, propertySelector.LookupOrValuelistStrategy); if (iLookupItem != -1) { listLookup.Add(iLookupItem); } } else { propertyValuesBuilder.Add( propertySelector.PropertyDef.ID, propertyDefType, szValue); } } // Lookup or MultiSelectLookup and found something if ((isLookup) && (listLookup.Count != 0)) { int[] arrLookupIDs = listLookup.ToArray(); propertyValuesBuilder.Add( propertySelector.PropertyDef.ID, propertyDefType, arrLookupIDs); } } #endregion else #region itterate XElements from XPath { List <int> listLookup = new List <int>(); var matchingPropertyElements = matchingElement.XPathSelectElements(propertySelector.XPathQuery); if (null == matchingPropertyElements) { continue; } // iterate found XElements foreach (var matchingPropertyElement in matchingPropertyElements) { if (null == matchingPropertyElement) { continue; } string szValue = matchingPropertyElement.Value; if (propertyDefType == MFDataType.MFDatatypeBoolean) { propertyValuesBuilder.Add( propertySelector.PropertyDef.ID, propertyDefType, CastToBool(szValue)); } else if (propertyDefType == MFDataType.MFDatatypeDate) { szValue = $"{szValue} 00:00:00"; propertyValuesBuilder.Add( propertySelector.PropertyDef.ID, propertyDefType, szValue); } else if (isLookup) { var iLookupDef = (propertySelector.LookupOrValuelistStrategy == LookupOrValuelistStrategy.SearchLookup ? propertySelector.LookupObjectDef.ID : propertySelector.LookupValueListDef.ID); var iLookupItem = LookupRef(vault, iLookupDef, propertyDefType, propertySelector.SearchByLookupID, szValue, propertySelector.LookupOrValuelistStrategy); if (iLookupItem != -1) { listLookup.Add(iLookupItem); } propertyValuesBuilder.AddLookup( propertySelector.PropertyDef.ID, szValue); } else { propertyValuesBuilder.Add( propertySelector.PropertyDef.ID, propertyDefType, szValue); } } // Lookup or MultiSelectLookup and found something if ((isLookup) && (listLookup.Count != 0)) { int[] arrLookupIDs = listLookup.ToArray(); propertyValuesBuilder.Add( propertySelector.PropertyDef.ID, propertyDefType, arrLookupIDs); } } #endregion // Add the property to the builder. //propertyValuesBuilder.Add( //propertySelector.PropertyDef.ID, //propertyDefType, //matchingPropertyElement.Value); } // Set the static values foreach (var staticPropertyValue in objectSelector.StaticPropertyValues ?? new List <StaticPropertyValue>()) { // Sanity. if (false == staticPropertyValue.PropertyDef.IsResolved) { throw new InvalidOperationException("The property value selector property definition is not resolved"); } // Find the property definition type. var propertyDefType = vault .PropertyDefOperations .GetPropertyDef(staticPropertyValue.PropertyDef.ID) .DataType; // Add the property to the builder. propertyValuesBuilder.Add( staticPropertyValue.PropertyDef.ID, propertyDefType, staticPropertyValue.Value); } // Create a reference to the parent? if (null != parent) { // If the property definition to use was configured then use that. if (true == objectSelector.ParentRelationshipPropertyDef?.IsResolved) { // Check that this property is a list and is for the correct object type. var parentRelationshipPropertyDef = vault .PropertyDefOperations .GetPropertyDef(objectSelector.ParentRelationshipPropertyDef.ID); if (false == parentRelationshipPropertyDef.BasedOnValueList || parentRelationshipPropertyDef.ValueList != parent.Type) { throw new InvalidOperationException( $"The property def {parentRelationshipPropertyDef.Name} ({parentRelationshipPropertyDef.ID}) is not based on value list {parent.Type}."); } // Use the configured property definition. propertyValuesBuilder.Add( parentRelationshipPropertyDef.ID, parentRelationshipPropertyDef.DataType, parent.ID); } else { // Retrieve data about the parent object type. var parentObjectType = vault .ObjectTypeOperations .GetObjectType(parent.Type); // Retrieve data about the child object type. var childObjectType = vault .ObjectTypeOperations .GetObjectType(objectSelector.ObjectType.ID); // Is there an owner for this child type? if (childObjectType.HasOwnerType) { // Use the "owner" property definition. propertyValuesBuilder.Add( parentObjectType.OwnerPropertyDef, MFDataType.MFDatatypeLookup, parent.ID); } else { // Use the default property definition. propertyValuesBuilder.Add( parentObjectType.DefaultPropertyDef, MFDataType.MFDatatypeMultiSelectLookup, parent.ID); } } } // Create a container for any attached files. var sourceObjectFiles = new SourceObjectFiles(); // Should we attach the file to this object? if (objectSelector.AttachFileToThisObject) { // Locate the files to retrieve. sourceObjectFiles = this.FindFilesToAttach(objectSelector, xmlFile, matchingElement, xmlNamespaceManager); // If we were supposed to attach a file but no files were found then throw an exception. if (objectSelector.AttachedFileConfiguration?.FileNotFoundHandlingStrategy == FileNotFoundHandlingStrategy.Fail && 0 == sourceObjectFiles.Count) { throw new InvalidOperationException("Attached file expected but not found."); } if (objectSelector.AttachedFileConfiguration?.AttachedFileHandlingStrategy == AttachedFileHandlingStrategy.AttachToCurrentObject) { // Retrieve information about the object type from the vault. var objectType = vault .ObjectTypeOperations .GetObjectType(objectSelector.ObjectType.ID); // If the object type cannot have files but we are meant to attach a file, then fail. if (false == objectType.CanHaveFiles) { throw new InvalidOperationException( $"The object type {objectType.NameSingular} cannot have files, but the configuration states to attach a file."); } } } // Which source object files should we use for the new object? var sourceObjectFilesForNewObject = objectSelector.AttachedFileConfiguration?.AttachedFileHandlingStrategy == AttachedFileHandlingStrategy.AttachToCurrentObject ? sourceObjectFiles : new SourceObjectFiles(); // Add the object to the vault. var createdObject = vault .ObjectOperations .CreateNewObjectEx( objectSelector.ObjectType.ID, propertyValuesBuilder.Values, sourceObjectFilesForNewObject, SFD: objectSelector.ObjectType.ID == (int)MFBuiltInObjectType.MFBuiltInObjectTypeDocument && sourceObjectFilesForNewObject.Count == 1 ); // The files which need to be deleted. attachedFilesToDelete.AddRange( sourceObjectFiles .Cast <SourceObjectFile>() .Select(sof => new FileInfo(sof.SourceFilePath)) ); // Are there any related objects (e.g. children) to create? foreach (var childObjectSelector in objectSelector.ChildObjectSelectors) { attachedFilesToDelete.AddRange(this.ImportXmlFile(vault, matchingElement, childObjectSelector, xmlFile: xmlFile, parent: createdObject.ObjVer, xmlNamespaceManager: xmlNamespaceManager)); } // Clean up the collections we were using. propertyValuesBuilder = new MFPropertyValuesBuilder(vault); // Handle creating a new object for the file. if ( objectSelector.AttachFileToThisObject && objectSelector.AttachedFileConfiguration?.AttachedFileHandlingStrategy == AttachedFileHandlingStrategy.CreateNewObject) { // Set the static values foreach (var staticPropertyValue in objectSelector.AttachedFileConfiguration?.StaticPropertyValues ?? new List <StaticPropertyValue>()) { // Sanity. if (false == staticPropertyValue.PropertyDef.IsResolved) { throw new InvalidOperationException("The property value selector property definition is not resolved"); } // Find the property definition type. var propertyDefType = vault .PropertyDefOperations .GetPropertyDef(staticPropertyValue.PropertyDef.ID) .DataType; // Add the property to the builder. propertyValuesBuilder.Add( staticPropertyValue.PropertyDef.ID, propertyDefType, staticPropertyValue.Value); } // Add the class property value. propertyValuesBuilder.SetClass(objectSelector.AttachedFileConfiguration.Class.ID); // Add a reference from this new object to the one we created earlier. { // Retrieve data about the parent object type. var parentObjectType = vault .ObjectTypeOperations .GetObjectType(createdObject.ObjVer.Type); // Set the relationship. propertyValuesBuilder.Add( parentObjectType.DefaultPropertyDef, MFDataType.MFDatatypeMultiSelectLookup, createdObject.ObjVer.ID); } // Add the object to the vault. var createdDocumentObject = vault .ObjectOperations .CreateNewObjectEx( objectSelector.AttachedFileConfiguration.ObjectType.ID, propertyValuesBuilder.Values, sourceObjectFiles, SFD: objectSelector.AttachedFileConfiguration.ObjectType.ID == (int)MFBuiltInObjectType.MFBuiltInObjectTypeDocument && sourceObjectFiles.Count == 1 ); } } // Return the files to remove. return(attachedFilesToDelete); }