/// <summary>
 /// Creates a WorkSession object that a host application can use to step through the process of presenting
 /// all the interviews and assembling all the documents that may result from the given template.
 ///
 /// Allows the default interview settings to be specified instead of being read from config file
 /// </summary>
 /// <param name="service">An object implementing the IServices interface, encapsulating the instance of
 /// HotDocs Server with which the host app is communicating.</param>
 /// <param name="template">The template upon which this WorkSession is based. The initial interview and/or
 /// document work items in the WorkSession will be based on this template (including its Switches property).</param>
 /// <param name="answers">A collection of XML answers to use as a starting point for the work session.
 /// The initial interview (if any) will be pre-populated with these answers, and the subsequent generation
 /// of documents will have access to these answers as well.</param>
 /// <param name="defaultInterviewSettings">The default interview settings to be used throughout the session</param>
 public WorkSession(IServices service, Template template, TextReader answers, InterviewSettings defaultInterviewSettings)
 {
     _service         = service;
     AnswerCollection = new AnswerCollection();
     if (answers != null)
     {
         AnswerCollection.ReadXml(answers);
     }
     DefaultAssemblySettings = new AssembleDocumentSettings();
     if (defaultInterviewSettings != null)
     {
         DefaultInterviewSettings = defaultInterviewSettings;
     }
     else
     {
         DefaultInterviewSettings = new InterviewSettings();
     }
     // add the work items
     _workItems = new List <WorkItem>();
     if (template.HasInterview)
     {
         _workItems.Add(new InterviewWorkItem(template));
     }
     if (template.GeneratesDocument)
     {
         _workItems.Add(new DocumentWorkItem(template));
     }
 }
        /// <summary>
        /// AssembleDocuments causes all contiguous pending document work items (from CurrentWorkItem onwards)
        /// to be assembled, and returns the assembled documents.
        /// </summary>
        /// <param name="preAssembleDocument">This delegate will be called immediately before each document is assembled.</param>
        /// <param name="postAssembleDocument">This delegate will be called immediately following assembly of each document.</param>
        /// <param name="userState">This object will be passed to the above delegates.</param>
        /// <include file="../Shared/Help.xml" path="Help/string/param[@name='logRef']"/>
        /// <returns>An array of Document, one item for each document that was assembled.  Note that these items
        /// are of type Document, not AssemblyResult (see below).</returns>
        /// <remarks>
        /// <para>If AssembleDocuments is called when the current work item is not a document (i.e. when there are
        /// currently no documents to assemble), it will return an empty array of results without performing any work.</para>
        /// TODO: include a table that shows the relationship between members of Document, AssemblyResult, WorkSession and DocumentWorkItem.
        /// </remarks>
        public Document[] AssembleDocuments(PreAssembleDocumentDelegate preAssembleDocument,
                                            PostAssembleDocumentDelegate postAssembleDocument, object userState, string logRef)
        {
            var result = new List <Document>();
            // skip past completed work items to get the current workItem
            WorkItem workItem  = null;
            int      itemIndex = 0;

            for (; itemIndex < _workItems.Count; itemIndex++)
            {
                workItem = _workItems[itemIndex];
                if (!workItem.IsCompleted)
                {
                    break;
                }
                workItem = null;
            }
            // while the current workItem != null && is a document (i.e. is not an interview)
            while (workItem != null && workItem is DocumentWorkItem)
            {
                var docWorkItem = workItem as DocumentWorkItem;
                // make a copy of the default assembly settings and pass it to the BeforeAssembleDocumentDelegate (if provided)
                AssembleDocumentSettings asmOpts = new AssembleDocumentSettings(DefaultAssemblySettings);
                asmOpts.Format = workItem.Template.NativeDocumentType;
                // if this is not the last work item in the queue, force retention of transient answers
                asmOpts.RetainTransientAnswers |= (workItem != _workItems[_workItems.Count - 1]);

                if (preAssembleDocument != null)
                {
                    preAssembleDocument(docWorkItem.Template, AnswerCollection, asmOpts, userState);
                }

                // assemble the item
                using (var asmResult = _service.AssembleDocument(docWorkItem.Template, new StringReader(AnswerCollection.XmlAnswers), asmOpts, logRef))
                {
                    if (postAssembleDocument != null)
                    {
                        postAssembleDocument(docWorkItem.Template, asmResult, userState);
                    }

                    // replace the session answers with the post-assembly answers
                    AnswerCollection.ReadXml(asmResult.Answers);
                    // add pendingAssemblies to the queue as necessary
                    InsertNewWorkItems(asmResult.PendingAssemblies, itemIndex);
                    // store UnansweredVariables in the DocumentWorkItem
                    docWorkItem.UnansweredVariables = asmResult.UnansweredVariables;
                    // add an appropriate Document to a list being compiled for the return value of this method
                    result.Add(asmResult.ExtractDocument());
                }
                // mark the current workitem as complete
                docWorkItem.IsCompleted = true;
                // advance to the next workitem
                workItem = (++itemIndex >= _workItems.Count) ? null : _workItems[itemIndex];
            }
            return(result.ToArray());
        }
        /// <summary>
        /// Called by the host application when answers have been posted back from a browser interview.
        /// </summary>
        /// <param name="interviewAnswers">The answers that were posted back from the interview.</param>
        public void FinishInterview(TextReader interviewAnswers)
        {
            // overlay interviewAnswers over the session answer set,
            AnswerCollection.OverlayXml(interviewAnswers);

            // skip past completed work items to get the current workItem
            WorkItem workItem  = null;
            int      itemIndex = 0;

            for (; itemIndex < _workItems.Count; itemIndex++)
            {
                workItem = _workItems[itemIndex];
                if (!workItem.IsCompleted)
                {
                    break;
                }
                workItem = null;
            }
            if (workItem != null && workItem is InterviewWorkItem)
            {
                // if the current template is an interview template
                if (workItem.Template.TemplateType == TemplateType.InterviewOnly)
                {
                    //     "assemble" it...
                    AssembleDocumentSettings asmOpts = new AssembleDocumentSettings(DefaultAssemblySettings);
                    asmOpts.Format = DocumentType.Native;
                    // if this is not the last work item in the queue, force retention of transient answers
                    asmOpts.RetainTransientAnswers |= (itemIndex < _workItems.Count - 1);

                    // assemble the item
                    using (var asmResult = _service.AssembleDocument(workItem.Template, new StringReader(AnswerCollection.XmlAnswers), asmOpts, ""))
                    {
                        // replace the session answers with the post-assembly answers
                        AnswerCollection.ReadXml(asmResult.Answers);
                        // add pendingAssemblies to the queue as necessary
                        InsertNewWorkItems(asmResult.PendingAssemblies, itemIndex);
                    }
                }
                // mark this interview workitem as complete.  (This will cause the WorkSession to advance to the next workItem.)
                CurrentWorkItem.IsCompleted = true;
            }
        }
        /// <summary>
        /// Assemble a document from the given template, answers and settings.
        /// </summary>
        /// <param name="template">An instance of the Template class.</param>
        /// <param name="answers">Either an XML answer string, or a string containing encoded
        /// interview answers as posted from a HotDocs browser interview.</param>
        /// <param name="settings">An instance of the AssembleDocumentResult class.</param>
        /// <include file="../Shared/Help.xml" path="Help/string/param[@name='logRef']"/>
        /// <returns>An AssemblyResult object containing all the files and data resulting from the request.</returns>
        public AssembleDocumentResult AssembleDocument(Template template, TextReader answers, AssembleDocumentSettings settings, string logRef)
        {
            // Validate input parameters, creating defaults as appropriate.
            string logStr = logRef == null ? string.Empty : logRef;
            if (template == null)
                throw new ArgumentNullException("template", string.Format(@"Local.Services.AssembleDocument: the ""template"" parameter passed in was null, logRef: {0}", logStr));

            if (settings == null)
                settings = new AssembleDocumentSettings();

            HotDocs.Server.AnswerCollection ansColl = new HotDocs.Server.AnswerCollection();
            ansColl.OverlayXMLAnswers(answers == null ? "" : answers.ReadToEnd());
            HotDocs.Server.OutputOptions outputOptions = ConvertOutputOptions(settings.OutputOptions);

            string docPath = CreateTempDocDirAndPath(template, settings.Format);
            _app.AssembleDocument(
                template.GetFullPath(),//Template path
                settings.UseMarkupSyntax ? hdsi.HDAssemblyOptions.asmOptMarkupView : hdsi.HDAssemblyOptions.asmOptNone,
                ansColl,
                docPath,
                outputOptions);

            //Prepare the post-assembly answer set (dropping transient ("don't save") answers when appropriate)
            HotDocs.Sdk.AnswerCollection resultAnsColl = new AnswerCollection();
            resultAnsColl.ReadXml(new StringReader(ansColl.XmlAnswers));
            string resultAnsXml = resultAnsColl.GetXMLString(false, settings.RetainTransientAnswers || _app.PendingAssemblyCmdLineStrings.Count > 0);

            //Build the list of pending assemblies.
            List<Template> pendingAssemblies = new List<Template>();
            for (int i = 0; i < _app.PendingAssemblyCmdLineStrings.Count; i++)
            {
                string cmdLine = _app.PendingAssemblyCmdLineStrings[i];
                string path, switches;
                Util.ParseHdAsmCmdLine(cmdLine, out path, out switches);
                pendingAssemblies.Add(new Template(Path.GetFileName(path), template.Location.Duplicate(), switches));
            }

            //Prepare the document stream and image information for the browser.
            DocumentType docType = settings.Format;
            List<NamedStream> supportingFiles = new List<NamedStream>();
            MemoryStream docStream;
            if (docType == DocumentType.Native)
            {
                docType = Document.GetDocumentType(docPath);
                docStream = LoadFileIntoMemStream(docPath);
            }
            else if (docType == DocumentType.HTMLwDataURIs)
            {
                //If the consumer requested both HTML and HTMLwDataURIs, they'll only get the latter.
                string content = Util.EmbedImagesInURIs(docPath);
                docStream = new MemoryStream(Encoding.UTF8.GetBytes(content));
            }
            else if (docType == DocumentType.MHTML)
            {
                string content = Util.HtmlToMultiPartMime(docPath);
                docStream = new MemoryStream(Encoding.UTF8.GetBytes(content));
            }
            else if (docType == DocumentType.HTML)
            {
                string targetFilenameNoExtention = Path.GetFileNameWithoutExtension(docPath);
                foreach (string img in Directory.EnumerateFiles(Path.GetDirectoryName(docPath)))
                {
                    string ext = Path.GetExtension(img).ToLower();
                    if (Path.GetFileName(img).StartsWith(targetFilenameNoExtention) && (ext == ".jpg" || ext == ".jpeg" || ext == ".gif" || ext == ".png" || ext == ".bmp"))
                        supportingFiles.Add(LoadFileIntoNamedStream(img));
                }

                docStream = LoadFileIntoMemStream(docPath);
            }
            else
            {
                docStream = LoadFileIntoMemStream(docPath);
            }

            //Now that we've loaded all of the assembly results into memory, remove the assembly files.
            FreeTempDocDir(docPath);

            //Return the results.
            Document document = new Document(template, docStream, docType, supportingFiles.ToArray(), _app.UnansweredVariablesList.ToArray());
            AssembleDocumentResult result = new AssembleDocumentResult(document, resultAnsXml, pendingAssemblies.ToArray(), _app.UnansweredVariablesList.ToArray());
            return result;
        }
        public void ReadXml()
        {
            AnswerCollection anss = new AnswerCollection();

            // this test checks some answer XML generated by browser interviews. It was problematic originally because of its empty <RptValue> elements
            // (expressed as paired open/close elements with nothing between them, rather than single elements with open/close combined in the same element)
            anss.ReadXml(@"<?xml version=""1.0"" standalone=""yes""?>
<AnswerSet title="""" version=""1.1"" useMangledNames=""false"">
	<Answer name=""Editor Full Name"">
		<TextValue unans=""true"" />
	</Answer>
	<Answer name=""Author Full Name"">
		<RptValue>
			<RptValue>
				<TextValue>A</TextValue>
				<TextValue unans=""true"" />
			</RptValue>
			<RptValue></RptValue>
		</RptValue>
	</Answer>
	<Answer name=""Book Title"">
		<RptValue>
			<RptValue>
				<RptValue>
					<TextValue>A</TextValue>
					<TextValue unans=""true"" />
				</RptValue>
				<RptValue></RptValue>
			</RptValue>
			<RptValue></RptValue>
		</RptValue>
	</Answer>
	<Answer name=""Date Completed"">
		<DateValue unans=""true"" />
	</Answer>
</AnswerSet>
");
            Assert.IsTrue(anss.AnswerCount == 4);
            Answer ans;

            Assert.IsFalse(anss.TryGetAnswer("Editor Full Name", Sdk.ValueType.Number, out ans));

            Assert.IsTrue(anss.TryGetAnswer("Editor Full Name", Sdk.ValueType.Text, out ans));
            Assert.IsFalse(ans.IsRepeated);
            Assert.IsTrue(ans.Save);
            Assert.IsTrue(ans.UserExtendible);
            Assert.IsTrue(ans.Type == Sdk.ValueType.Text);
            Assert.IsFalse(ans.GetAnswered());
            Assert.IsFalse(ans.GetValue <TextValue>().IsAnswered);
            Assert.IsTrue(ans.GetValue <TextValue>().Type == Sdk.ValueType.Text);
            Assert.IsTrue(ans.GetValue <TextValue>().UserModifiable);

            Assert.IsFalse(anss.TryGetAnswer("author full name", Sdk.ValueType.Text, out ans));

            Assert.IsTrue(anss.TryGetAnswer("Author Full Name", Sdk.ValueType.Text, out ans));
            Assert.IsTrue(ans.IsRepeated);
            Assert.IsTrue(ans.GetChildCount() == 1);
            Assert.IsTrue(ans.GetChildCount(0) == 1);
            Assert.IsTrue(ans.GetChildCount(1) == 0);
            Assert.IsTrue(ans.GetValue <TextValue>(0, 0).Value == "A");
            Assert.IsFalse(ans.GetValue <TextValue>(0, 1).IsAnswered);
            Assert.IsFalse(ans.GetValue <TextValue>(1).IsAnswered);
            Assert.IsFalse(ans.GetValue <TextValue>(1, 0).IsAnswered);
            // unusual HotDocs indexing rules
            Assert.IsTrue(ans.GetValue <TextValue>().IsAnswered);
            Assert.IsTrue(ans.GetValue <TextValue>().Value == "A");
            Assert.IsTrue(ans.GetValue <TextValue>(0).IsAnswered);
            Assert.IsTrue(ans.GetValue <TextValue>(0).Value == "A");
            Assert.IsTrue(ans.GetValue <TextValue>(0, 0, 0).IsAnswered);
            Assert.IsTrue(ans.GetValue <TextValue>(0, 0, 0).Value == "A");

            Assert.IsFalse(anss.TryGetAnswer("does not exist", Sdk.ValueType.Text, out ans));
        }
        public void ReadXml()
        {
            AnswerCollection anss = new AnswerCollection();
            // this test checks some answer XML generated by browser interviews. It was problematic originally because of its empty <RptValue> elements
            // (expressed as paired open/close elements with nothing between them, rather than single elements with open/close combined in the same element)
            anss.ReadXml(@"<?xml version=""1.0"" standalone=""yes""?>
            <AnswerSet title="""" version=""1.1"" useMangledNames=""false"">
            <Answer name=""Editor Full Name"">
            <TextValue unans=""true"" />
            </Answer>
            <Answer name=""Author Full Name"">
            <RptValue>
            <RptValue>
                <TextValue>A</TextValue>
                <TextValue unans=""true"" />
            </RptValue>
            <RptValue></RptValue>
            </RptValue>
            </Answer>
            <Answer name=""Book Title"">
            <RptValue>
            <RptValue>
                <RptValue>
                    <TextValue>A</TextValue>
                    <TextValue unans=""true"" />
                </RptValue>
                <RptValue></RptValue>
            </RptValue>
            <RptValue></RptValue>
            </RptValue>
            </Answer>
            <Answer name=""Date Completed"">
            <DateValue unans=""true"" />
            </Answer>
            </AnswerSet>
            ");
            Assert.IsTrue(anss.AnswerCount == 4);
            Answer ans;
            Assert.IsTrue(anss.TryGetAnswer("Editor Full Name", out ans));
            Assert.IsFalse(ans.IsRepeated);
            Assert.IsTrue(ans.Save);
            Assert.IsTrue(ans.UserExtendible);
            Assert.IsTrue(ans.Type == Sdk.ValueType.Text);
            Assert.IsFalse(ans.GetAnswered());
            Assert.IsFalse(ans.GetValue<TextValue>().IsAnswered);
            Assert.IsTrue(ans.GetValue<TextValue>().Type == Sdk.ValueType.Text);
            Assert.IsTrue(ans.GetValue<TextValue>().UserModifiable);

            Assert.IsFalse(anss.TryGetAnswer("author full name", out ans));

            Assert.IsTrue(anss.TryGetAnswer("Author Full Name", out ans));
            Assert.IsTrue(ans.IsRepeated);
            Assert.IsTrue(ans.GetChildCount() == 1);
            Assert.IsTrue(ans.GetChildCount(0) == 1);
            Assert.IsTrue(ans.GetChildCount(1) == 0);
            Assert.IsTrue(ans.GetValue<TextValue>(0, 0).Value == "A");
            Assert.IsFalse(ans.GetValue<TextValue>(0, 1).IsAnswered);
            Assert.IsFalse(ans.GetValue<TextValue>(1).IsAnswered);
            Assert.IsFalse(ans.GetValue<TextValue>(1, 0).IsAnswered);
            // unusual HotDocs indexing rules
            Assert.IsTrue(ans.GetValue<TextValue>().IsAnswered);
            Assert.IsTrue(ans.GetValue<TextValue>().Value == "A");
            Assert.IsTrue(ans.GetValue<TextValue>(0).IsAnswered);
            Assert.IsTrue(ans.GetValue<TextValue>(0).Value == "A");
            Assert.IsTrue(ans.GetValue<TextValue>(0, 0, 0).IsAnswered);
            Assert.IsTrue(ans.GetValue<TextValue>(0, 0, 0).Value == "A");

            Assert.IsFalse(anss.TryGetAnswer("does not exist", out ans));
        }
Example #7
0
        private static void LoadAnswerFileDataSource(AnswerFileDataSource answerFileDataSource)
        {
            Debug.Assert(s_readerWriterLock.IsWriteLockHeld);

            Stream answerFileStream = null;

            try
            {
                answerFileStream = new FileStream(answerFileDataSource.AnswerFilePath, FileMode.Open, FileAccess.Read);

                AnswerCollection answerSet = new AnswerCollection();
                answerSet.ReadXml(answerFileStream);

                ResourceSet   resourceSet  = s_metadata.ResourceSets.Single(rs => rs.Name == answerFileDataSource.ResourceId);
                ResourceType  resourceType = resourceSet.ResourceType;
                List <Answer> answers      = new List <Answer>(resourceType.Properties.Count);
                int           repeatCount  = 0;
                Answer        answer;
                foreach (var property in resourceType.Properties)
                {
                    if (((property.Kind & ResourcePropertyKind.Key) != ResourcePropertyKind.Key) &&
                        answerSet.TryGetAnswer(answerFileDataSource.PropertyNameToSourceNameMap[property.Name], out answer))
                    {
                        // Sanity check to be sure the property type the metadata is expecting is the same as the values in the answer file.
                        Type type;
                        switch (answer.Type)
                        {
                        case ValueType.Text:
                        case ValueType.MultipleChoice:
                            type = typeof(string);
                            break;

                        case ValueType.Number:
                            type = typeof(double?);
                            break;

                        case ValueType.Date:
                            type = typeof(DateTime?);
                            break;

                        case ValueType.TrueFalse:
                            type = typeof(bool?);
                            break;

                        default:
                            throw new Exception(string.Format("The value type '{0}' is not supported.", answer.Type.ToString()));
                        }

                        if (property.ResourceType.InstanceType != type)
                        {
                            throw new Exception(string.Format("The type of the metadata property '{0}' does not match the type of the " +
                                                              "corresponding answer '{1}'.", answerFileDataSource.PropertyNameToSourceNameMap[property.Name], answer.Name));
                        }

                        repeatCount = Math.Max(repeatCount, answer.GetChildCount());
                        answers.Add(answer);
                    }
                    else
                    {
                        answers.Add(null);
                    }
                }

                // Populate the data source with data.
                IList <DSPResource> resourceList = s_context.GetResourceSetEntities(resourceSet.Name);
                resourceList.Clear();

                for (int repeatIndex = 0; repeatIndex < repeatCount; repeatIndex++)
                {
                    var resource = new DSPResource(resourceSet.ResourceType, s_readerWriterLock);

                    for (int propertyIndex = 0; propertyIndex < resourceType.Properties.Count; propertyIndex++)
                    {
                        ResourceProperty property = resourceType.Properties[propertyIndex];
                        object           value;
                        if ((property.Kind & ResourcePropertyKind.Key) == ResourcePropertyKind.Key)
                        {
                            value = repeatIndex + 1;
                        }
                        else
                        {
                            IValue iValue = null;
                            answer = answers[propertyIndex];
                            if ((answer != null) && (repeatIndex <= answer.GetChildCount()))
                            {
                                iValue = answer.GetValue(repeatIndex);
                            }

                            if (property.ResourceType.InstanceType == typeof(string))
                            {
                                value = ((iValue != null) && iValue.IsAnswered) ? iValue.ToString(null) : string.Empty;
                            }
                            else if (property.ResourceType.InstanceType == typeof(double?))
                            {
                                if ((iValue != null) && iValue.IsAnswered)
                                {
                                    value = iValue.ToDouble(null);
                                }
                                else
                                {
                                    value = null;
                                }
                            }
                            else if (property.ResourceType.InstanceType == typeof(DateTime?))
                            {
                                if ((iValue != null) && iValue.IsAnswered)
                                {
                                    value = iValue.ToDateTime(null);
                                }
                                else
                                {
                                    value = null;
                                }
                            }
                            else
                            {
                                Debug.Assert(property.ResourceType.InstanceType == typeof(bool?));
                                if ((iValue != null) && iValue.IsAnswered)
                                {
                                    value = iValue.ToBoolean(null);
                                }
                                else
                                {
                                    value = null;
                                }
                            }
                        }

                        resource.SetValue(property.Name, value);
                    }

                    resourceList.Add(resource);
                }
            }
            catch (Exception e)
            {
                throw new DataServiceException(string.Format("Failed to read the answers for the data source key '{0}' from the answer file '{1}'.",
                                                             answerFileDataSource.DataSourceId, answerFileDataSource.DataSourceName), e);
            }
            finally
            {
                if (answerFileStream != null)
                {
                    answerFileStream.Close();
                }
            }
        }
        /// <summary>
        /// Assemble a document from the given template, answers and settings.
        /// </summary>
        /// <param name="template">An instance of the Template class.</param>
        /// <param name="answers">Either an XML answer string, or a string containing encoded
        /// interview answers as posted from a HotDocs browser interview.</param>
        /// <param name="settings">An instance of the AssembleDocumentResult class.</param>
        /// <include file="../Shared/Help.xml" path="Help/string/param[@name='logRef']"/>
        /// <returns>An AssemblyResult object containing all the files and data resulting from the request.</returns>
        public AssembleDocumentResult AssembleDocument(Template template, TextReader answers, AssembleDocumentSettings settings, string logRef)
        {
            // Validate input parameters, creating defaults as appropriate.
            string logStr = logRef == null ? string.Empty : logRef;

            if (template == null)
            {
                throw new ArgumentNullException("template", string.Format(@"Local.Services.AssembleDocument: the ""template"" parameter passed in was null, logRef: {0}", logStr));
            }

            if (settings == null)
            {
                settings = new AssembleDocumentSettings();
            }


            HotDocs.Server.AnswerCollection ansColl = new HotDocs.Server.AnswerCollection();
            ansColl.OverlayXMLAnswers(answers == null ? "" : answers.ReadToEnd());
            HotDocs.Server.OutputOptions outputOptions = ConvertOutputOptions(settings.OutputOptions);

            string docPath = CreateTempDocDirAndPath(template, settings.Format);

            _app.AssembleDocument(
                template.GetFullPath(),                //Template path
                hdsi.HDAssemblyOptions.asmOptMarkupView,
                ansColl,
                docPath,
                outputOptions);

            //Prepare the post-assembly answer set.
            HotDocs.Sdk.AnswerCollection resultAnsColl = new AnswerCollection();
            resultAnsColl.ReadXml(new StringReader(ansColl.XmlAnswers));
            if (!settings.RetainTransientAnswers && _app.PendingAssemblyCmdLineStrings.Count == 0)
            {
                // Create a list of all "transient" answers to remove.
                IEnumerable <Answer> transAnswers    = from a in resultAnsColl where !a.Save select a;
                List <string>        answersToRemove = new List <string>();
                foreach (Answer ans in transAnswers)
                {
                    answersToRemove.Add(ans.Name);
                }

                // Iterate through the list of answers to remove and remove them from the result answer collection.
                // This is done as a separate step so we are not modifying the collecion over which we are iterating.
                foreach (string s in answersToRemove)
                {
                    resultAnsColl.RemoveAnswer(s);
                }
            }

            //Build the list of pending assemblies.
            List <Template> pendingAssemblies = new List <Template>();

            for (int i = 0; i < _app.PendingAssemblyCmdLineStrings.Count; i++)
            {
                string cmdLine = _app.PendingAssemblyCmdLineStrings[i];
                string path, switches;
                Util.ParseHdAsmCmdLine(cmdLine, out path, out switches);
                pendingAssemblies.Add(new Template(Path.GetFileName(path), template.Location.Duplicate(), switches));
            }

            //Prepare the document stream and image information for the browser.
            DocumentType       docType         = settings.Format;
            List <NamedStream> supportingFiles = new List <NamedStream>();
            MemoryStream       docStream;

            if (docType == DocumentType.Native)
            {
                docType   = Document.GetDocumentType(docPath);
                docStream = LoadFileIntoMemStream(docPath);
            }
            else if (docType == DocumentType.HTMLwDataURIs)
            {
                //If the consumer requested both HTML and HTMLwDataURIs, they'll only get the latter.
                string content = Util.EmbedImagesInURIs(docPath);
                docStream = new MemoryStream(Encoding.UTF8.GetBytes(content));
            }
            else if (docType == DocumentType.MHTML)
            {
                string content = Util.HtmlToMultiPartMime(docPath);
                docStream = new MemoryStream(Encoding.UTF8.GetBytes(content));
            }
            else if (docType == DocumentType.HTML)
            {
                string targetFilenameNoExtention = Path.GetFileNameWithoutExtension(docPath);
                foreach (string img in Directory.EnumerateFiles(Path.GetDirectoryName(docPath)))
                {
                    string ext = Path.GetExtension(img).ToLower();
                    if (Path.GetFileName(img).StartsWith(targetFilenameNoExtention) && (ext == ".jpg" || ext == ".jpeg" || ext == ".gif" || ext == ".png" || ext == ".bmp"))
                    {
                        supportingFiles.Add(LoadFileIntoNamedStream(img));
                    }
                }

                docStream = LoadFileIntoMemStream(docPath);
            }
            else
            {
                docStream = LoadFileIntoMemStream(docPath);
            }

            //Now that we've loaded all of the assembly results into memory, remove the assembly files.
            FreeTempDocDir(docPath);

            //Return the results.
            Document document             = new Document(template, docStream, docType, supportingFiles.ToArray(), _app.UnansweredVariablesList.ToArray());
            AssembleDocumentResult result = new AssembleDocumentResult(document, resultAnsColl.XmlAnswers, pendingAssemblies.ToArray(), _app.UnansweredVariablesList.ToArray());

            return(result);
        }
Example #9
0
 /// <summary>
 /// Creates a WorkSession object that a host application can use to step through the process of presenting
 /// all the interviews and assembling all the documents that may result from the given template.
 /// </summary>
 /// <param name="service">An object implementing the IServices interface, encapsulating the instance of
 /// HotDocs Server with which the host app is communicating.</param>
 /// <param name="template">The template upon which this WorkSession is based. The initial interview and/or
 /// document work items in the WorkSession will be based on this template (including its Switches property).</param>
 /// <param name="answers">A collection of XML answers to use as a starting point for the work session.
 /// The initial interview (if any) will be pre-populated with these answers, and the subsequent generation
 /// of documents will have access to these answers as well.</param>
 public WorkSession(IServices service, Template template, TextReader answers)
 {
     _service = service;
     AnswerCollection = new AnswerCollection();
     if (answers != null)
         AnswerCollection.ReadXml(answers);
     DefaultAssemblySettings = new AssembleDocumentSettings();
     DefaultInterviewSettings = new InterviewSettings();
     // add the work items
     _workItems = new List<WorkItem>();
     if (template.HasInterview)
         _workItems.Add(new InterviewWorkItem(template));
     if (template.GeneratesDocument)
         _workItems.Add(new DocumentWorkItem(template));
 }
Example #10
0
        public void ReadXml()
        {
            AnswerCollection anss = new AnswerCollection();

            // this test checks some answer XML generated by browser interviews. It was problematic originally because of its empty <RptValue> elements
            // (expressed as paired open/close elements with nothing between them, rather than single elements with open/close combined in the same element)
            // NOTE: added a lot of oddly formed and malformed answers to this answer collection, for purposes of
            // trying to ensure that we read answer XML in a more robust manner.  Some 3rd parties generate XML
            // that is schema valid, but is otherwise (by HotDocs standards) quite odd.  For example, answers marked
            // "unanswered" but which have answer data present, or which use collapsed XML elements in ways
            // HotDocs typically does not.
            anss.ReadXml(@"<?xml version=""1.0"" standalone=""yes""?>
<AnswerSet title="""" version=""1.1"" useMangledNames=""false"">
	<Answer name=""Editor Full Name"">
		<TextValue unans=""true"" />
	</Answer>
	<Answer name=""Text000"">
		<TextValue/>
	</Answer>
	<Answer name=""Text010"">
		<TextValue></TextValue>
	</Answer>
	<Answer name=""Text020"">
		<TextValue>Test Answer</TextValue>
	</Answer>
	<Answer name=""Text030"">
		<TextValue>
    Another
Test Answer
        </TextValue>
	</Answer>
	<Answer name=""Text040"">
		<TextValue unans=""true""></TextValue>
	</Answer>
	<Answer name=""Text050"">
		<TextValue unans=""true"">An Invalid Test Answer</TextValue>
	</Answer>
	<Answer name=""Number000"">
		<NumValue/>
	</Answer>
	<Answer name=""Number010"">
		<NumValue unans=""true"" />
	</Answer>
	<Answer name=""Number020"">
		<NumValue unans=""true""></NumValue>
	</Answer>
	<Answer name=""Number030"">
		<NumValue>5.0000000</NumValue>
	</Answer>
	<Answer name=""Number040"">
		<NumValue>123</NumValue>
	</Answer>
	<Answer name=""Number050"">
		<NumValue unans=""true"">234 or anything else</NumValue>
	</Answer>
	<Answer name=""Date000"">
		<DateValue/>
	</Answer>
	<Answer name=""Date010"">
		<DateValue></DateValue>
	</Answer>
	<Answer name=""Date020"">
		<DateValue unans=""true"" />
	</Answer>
	<Answer name=""Date030"">
		<DateValue unans=""true"" ></DateValue>
	</Answer>
	<Answer name=""Date Completed"">
		<DateValue>31-10-2015</DateValue>
	</Answer>
	<Answer name=""TF000"">
		<TFValue/>
	</Answer>
	<Answer name=""TF010"">
		<TFValue></TFValue>
	</Answer>
	<Answer name=""TF020"">
		<TFValue unans=""true"" />
	</Answer>
	<Answer name=""TF030"">
		<TFValue unans=""true""></TFValue>
	</Answer>
	<Answer name=""TF040"">
		<TFValue>true</TFValue>
	</Answer>
	<Answer name=""TF050"">
		<TFValue unans=""false"">False</TFValue>
	</Answer>
	<Answer name=""TF060"">
		<TFValue unans=""true"">Ignored</TFValue>
	</Answer>
    <Answer name=""MCVar000"">
        <MCValue/>
    </Answer>
    <Answer name=""MCVar010"">
        <MCValue unans=""true"" />
    </Answer>
    <Answer name=""MCVar020"">
        <MCValue></MCValue>
    </Answer>
    <Answer name=""MCVar030"">
        <MCValue unans=""true""></MCValue>
    </Answer>
    <Answer name=""MCVar031"">
        <MCValue>
            <SelValue/>
        </MCValue>
    </Answer>
    <Answer name=""MCVar032"">
        <MCValue unans=""true"">
            <SelValue unans=""true""/>
        </MCValue>
    </Answer>
    <Answer name=""MCVar033"">
        <MCValue unans=""true"">
            <SelValue></SelValue>
        </MCValue>
    </Answer>
    <Answer name=""MCVar034"">
        <MCValue unans=""true"">
            <SelValue unans=""true""></SelValue>
        </MCValue>
    </Answer>
    <Answer name=""MCVar035"">
        <MCValue unans=""true"">
            <SelValue>sel1</SelValue>
        </MCValue>
    </Answer>
    <Answer name=""MCVar040"">
        <MCValue>
            <SelValue/>
        </MCValue>
    </Answer>
    <Answer name=""MCVar050"">
        <MCValue>
            <SelValue unans=""true""/>
        </MCValue>
    </Answer>
    <Answer name=""MCVar060"">
        <MCValue>
            <SelValue></SelValue>
        </MCValue>
    </Answer>
    <Answer name=""MCVar070"">
        <MCValue>
            <SelValue unans=""true""></SelValue>
        </MCValue>
    </Answer>
    <Answer name=""MCVar080"">
        <MCValue>
            <SelValue>sel1</SelValue>
            <SelValue>sel2</SelValue>
        </MCValue>
    </Answer>
    <Answer name=""MCVar090"">
        <MCValue>
            <SelValue>sel1</SelValue>
            <SelValue></SelValue>
        </MCValue>
    </Answer>
    <Answer name=""MCVar100"">
        <MCValue>
            <SelValue>sel1</SelValue>
            <SelValue/>
        </MCValue>
    </Answer>
    <Answer name=""MCVar110"">
        <MCValue>
            <SelValue>sel1</SelValue>
            <SelValue unans=""true""></SelValue>
        </MCValue>
    </Answer>
    <Answer name=""MCVar120"">
        <MCValue>
            <SelValue>sel1</SelValue>
            <SelValue unans=""true""/>
        </MCValue>
    </Answer>
    <Answer name=""MCVar130"">
        <MCValue>
            <SelValue>sel1</SelValue>
            <SelValue></SelValue>
            <SelValue>sel3</SelValue>
        </MCValue>
    </Answer>
    <Answer name=""MCVar140"">
        <MCValue>
            <SelValue>sel1</SelValue>
            <SelValue/>
            <SelValue>sel3</SelValue>
        </MCValue>
    </Answer>
    <Answer name=""MCVar150"">
        <MCValue>
            <SelValue>sel1</SelValue>
            <SelValue unans=""true""></SelValue>
            <SelValue>sel3</SelValue>
        </MCValue>
    </Answer>
    <Answer name=""MCVar160"">
        <MCValue>
            <SelValue>sel1</SelValue>
            <SelValue unans=""true""/>
            <SelValue>sel3</SelValue>
        </MCValue>
    </Answer>
    <Answer name=""MCVar170"">
        <MCValue>
            <SelValue>sel1</SelValue>
            <SelValue unans=""true"">sel2</SelValue>
        </MCValue>
    </Answer>
    <Answer name=""MCVar180"">
        <MCValue>
            <SelValue unans=""true"">sel1</SelValue>
            <SelValue unans=""true"">sel2</SelValue>
        </MCValue>
    </Answer>
    <Answer name=""MCVar190"">
        <MCValue>
            <SelValue unans=""true"">sel1</SelValue>
            <SelValue>sel2</SelValue>
        </MCValue>
    </Answer>
	<Answer name=""Author Full Name"">
		<RptValue>
			<RptValue>
				<TextValue>A</TextValue>
				<TextValue unans=""true"" />
			</RptValue>
			<RptValue></RptValue>
		</RptValue>
	</Answer>
	<Answer name=""Book Title"">
		<RptValue>
			<RptValue>
				<RptValue>
					<TextValue>A</TextValue>
					<TextValue unans=""true"" />
				</RptValue>
				<RptValue></RptValue>
			</RptValue>
			<RptValue></RptValue>
		</RptValue>
	</Answer>
</AnswerSet>
");
            Assert.IsTrue(anss.AnswerCount == 52);
            Answer ans;

            // ensure that lookup of incorrect typed answer fails
            Assert.IsFalse(anss.TryGetAnswer("Editor Full Name", Sdk.ValueType.Number, out ans));

            // test various attributes of an answer
            Assert.IsTrue(anss.TryGetAnswer("Editor Full Name", Sdk.ValueType.Text, out ans));
            Assert.IsFalse(ans.IsRepeated);
            Assert.IsTrue(ans.Save);
            Assert.IsTrue(ans.UserExtendible);
            Assert.IsTrue(ans.Type == Sdk.ValueType.Text);
            Assert.IsFalse(ans.GetAnswered());
            Assert.IsFalse(ans.GetValue <TextValue>().IsAnswered);
            Assert.IsTrue(ans.GetValue <TextValue>().Type == Sdk.ValueType.Text);
            Assert.IsTrue(ans.GetValue <TextValue>().UserModifiable);

            // ensure lookup of name with incorrect casing fails
            Assert.IsFalse(anss.TryGetAnswer("author full name", Sdk.ValueType.Text, out ans));

            // check some repeated answer indexing rules
            Assert.IsTrue(anss.TryGetAnswer("Author Full Name", Sdk.ValueType.Text, out ans));
            Assert.IsTrue(ans.IsRepeated);
            Assert.IsTrue(ans.GetChildCount() == 1);
            Assert.IsTrue(ans.GetChildCount(0) == 1);
            Assert.IsTrue(ans.GetChildCount(1) == 0);
            Assert.IsTrue(ans.GetValue <TextValue>(0, 0).Value == "A");
            Assert.IsFalse(ans.GetValue <TextValue>(0, 1).IsAnswered);
            Assert.IsFalse(ans.GetValue <TextValue>(1).IsAnswered);
            Assert.IsFalse(ans.GetValue <TextValue>(1, 0).IsAnswered);
            // unusual HotDocs indexing rules
            Assert.IsTrue(ans.GetValue <TextValue>().IsAnswered);
            Assert.IsTrue(ans.GetValue <TextValue>().Value == "A");
            Assert.IsTrue(ans.GetValue <TextValue>(0).IsAnswered);
            Assert.IsTrue(ans.GetValue <TextValue>(0).Value == "A");
            Assert.IsTrue(ans.GetValue <TextValue>(0, 0, 0).IsAnswered);
            Assert.IsTrue(ans.GetValue <TextValue>(0, 0, 0).Value == "A");

            // ensure that lookup of non-existing answer fails
            Assert.IsFalse(anss.TryGetAnswer("does not exist", Sdk.ValueType.Text, out ans));
        }
        public void ReadXml()
        {
            AnswerCollection anss = new AnswerCollection();
            // this test checks some answer XML generated by browser interviews. It was problematic originally because of its empty <RptValue> elements
            // (expressed as paired open/close elements with nothing between them, rather than single elements with open/close combined in the same element)
            // NOTE: added a lot of oddly formed and malformed answers to this answer collection, for purposes of
            // trying to ensure that we read answer XML in a more robust manner.  Some 3rd parties generate XML
            // that is schema valid, but is otherwise (by HotDocs standards) quite odd.  For example, answers marked
            // "unanswered" but which have answer data present, or which use collapsed XML elements in ways
            // HotDocs typically does not.
            anss.ReadXml(@"<?xml version=""1.0"" standalone=""yes""?>
            <AnswerSet title="""" version=""1.1"" useMangledNames=""false"">
            <Answer name=""Editor Full Name"">
            <TextValue unans=""true"" />
            </Answer>
            <Answer name=""Text000"">
            <TextValue/>
            </Answer>
            <Answer name=""Text010"">
            <TextValue></TextValue>
            </Answer>
            <Answer name=""Text020"">
            <TextValue>Test Answer</TextValue>
            </Answer>
            <Answer name=""Text030"">
            <TextValue>
            Another
            Test Answer
            </TextValue>
            </Answer>
            <Answer name=""Text040"">
            <TextValue unans=""true""></TextValue>
            </Answer>
            <Answer name=""Text050"">
            <TextValue unans=""true"">An Invalid Test Answer</TextValue>
            </Answer>
            <Answer name=""Number000"">
            <NumValue/>
            </Answer>
            <Answer name=""Number010"">
            <NumValue unans=""true"" />
            </Answer>
            <Answer name=""Number020"">
            <NumValue unans=""true""></NumValue>
            </Answer>
            <Answer name=""Number030"">
            <NumValue>5.0000000</NumValue>
            </Answer>
            <Answer name=""Number040"">
            <NumValue>123</NumValue>
            </Answer>
            <Answer name=""Number050"">
            <NumValue unans=""true"">234 or anything else</NumValue>
            </Answer>
            <Answer name=""Date000"">
            <DateValue/>
            </Answer>
            <Answer name=""Date010"">
            <DateValue></DateValue>
            </Answer>
            <Answer name=""Date020"">
            <DateValue unans=""true"" />
            </Answer>
            <Answer name=""Date030"">
            <DateValue unans=""true"" ></DateValue>
            </Answer>
            <Answer name=""Date Completed"">
            <DateValue>31-10-2015</DateValue>
            </Answer>
            <Answer name=""TF000"">
            <TFValue/>
            </Answer>
            <Answer name=""TF010"">
            <TFValue></TFValue>
            </Answer>
            <Answer name=""TF020"">
            <TFValue unans=""true"" />
            </Answer>
            <Answer name=""TF030"">
            <TFValue unans=""true""></TFValue>
            </Answer>
            <Answer name=""TF040"">
            <TFValue>true</TFValue>
            </Answer>
            <Answer name=""TF050"">
            <TFValue unans=""false"">False</TFValue>
            </Answer>
            <Answer name=""TF060"">
            <TFValue unans=""true"">Ignored</TFValue>
            </Answer>
            <Answer name=""MCVar000"">
            <MCValue/>
            </Answer>
            <Answer name=""MCVar010"">
            <MCValue unans=""true"" />
            </Answer>
            <Answer name=""MCVar020"">
            <MCValue></MCValue>
            </Answer>
            <Answer name=""MCVar030"">
            <MCValue unans=""true""></MCValue>
            </Answer>
            <Answer name=""MCVar031"">
            <MCValue>
            <SelValue/>
            </MCValue>
            </Answer>
            <Answer name=""MCVar032"">
            <MCValue unans=""true"">
            <SelValue unans=""true""/>
            </MCValue>
            </Answer>
            <Answer name=""MCVar033"">
            <MCValue unans=""true"">
            <SelValue></SelValue>
            </MCValue>
            </Answer>
            <Answer name=""MCVar034"">
            <MCValue unans=""true"">
            <SelValue unans=""true""></SelValue>
            </MCValue>
            </Answer>
            <Answer name=""MCVar035"">
            <MCValue unans=""true"">
            <SelValue>sel1</SelValue>
            </MCValue>
            </Answer>
            <Answer name=""MCVar040"">
            <MCValue>
            <SelValue/>
            </MCValue>
            </Answer>
            <Answer name=""MCVar050"">
            <MCValue>
            <SelValue unans=""true""/>
            </MCValue>
            </Answer>
            <Answer name=""MCVar060"">
            <MCValue>
            <SelValue></SelValue>
            </MCValue>
            </Answer>
            <Answer name=""MCVar070"">
            <MCValue>
            <SelValue unans=""true""></SelValue>
            </MCValue>
            </Answer>
            <Answer name=""MCVar080"">
            <MCValue>
            <SelValue>sel1</SelValue>
            <SelValue>sel2</SelValue>
            </MCValue>
            </Answer>
            <Answer name=""MCVar090"">
            <MCValue>
            <SelValue>sel1</SelValue>
            <SelValue></SelValue>
            </MCValue>
            </Answer>
            <Answer name=""MCVar100"">
            <MCValue>
            <SelValue>sel1</SelValue>
            <SelValue/>
            </MCValue>
            </Answer>
            <Answer name=""MCVar110"">
            <MCValue>
            <SelValue>sel1</SelValue>
            <SelValue unans=""true""></SelValue>
            </MCValue>
            </Answer>
            <Answer name=""MCVar120"">
            <MCValue>
            <SelValue>sel1</SelValue>
            <SelValue unans=""true""/>
            </MCValue>
            </Answer>
            <Answer name=""MCVar130"">
            <MCValue>
            <SelValue>sel1</SelValue>
            <SelValue></SelValue>
            <SelValue>sel3</SelValue>
            </MCValue>
            </Answer>
            <Answer name=""MCVar140"">
            <MCValue>
            <SelValue>sel1</SelValue>
            <SelValue/>
            <SelValue>sel3</SelValue>
            </MCValue>
            </Answer>
            <Answer name=""MCVar150"">
            <MCValue>
            <SelValue>sel1</SelValue>
            <SelValue unans=""true""></SelValue>
            <SelValue>sel3</SelValue>
            </MCValue>
            </Answer>
            <Answer name=""MCVar160"">
            <MCValue>
            <SelValue>sel1</SelValue>
            <SelValue unans=""true""/>
            <SelValue>sel3</SelValue>
            </MCValue>
            </Answer>
            <Answer name=""MCVar170"">
            <MCValue>
            <SelValue>sel1</SelValue>
            <SelValue unans=""true"">sel2</SelValue>
            </MCValue>
            </Answer>
            <Answer name=""MCVar180"">
            <MCValue>
            <SelValue unans=""true"">sel1</SelValue>
            <SelValue unans=""true"">sel2</SelValue>
            </MCValue>
            </Answer>
            <Answer name=""MCVar190"">
            <MCValue>
            <SelValue unans=""true"">sel1</SelValue>
            <SelValue>sel2</SelValue>
            </MCValue>
            </Answer>
            <Answer name=""Author Full Name"">
            <RptValue>
            <RptValue>
                <TextValue>A</TextValue>
                <TextValue unans=""true"" />
            </RptValue>
            <RptValue></RptValue>
            </RptValue>
            </Answer>
            <Answer name=""Book Title"">
            <RptValue>
            <RptValue>
                <RptValue>
                    <TextValue>A</TextValue>
                    <TextValue unans=""true"" />
                </RptValue>
                <RptValue></RptValue>
            </RptValue>
            <RptValue></RptValue>
            </RptValue>
            </Answer>
            </AnswerSet>
            ");
            Assert.IsTrue(anss.AnswerCount == 52);
            Answer ans;

            // ensure that lookup of incorrect typed answer fails
            Assert.IsFalse(anss.TryGetAnswer("Editor Full Name", Sdk.ValueType.Number, out ans));

            // test various attributes of an answer
            Assert.IsTrue(anss.TryGetAnswer("Editor Full Name", Sdk.ValueType.Text, out ans));
            Assert.IsFalse(ans.IsRepeated);
            Assert.IsTrue(ans.Save);
            Assert.IsTrue(ans.UserExtendible);
            Assert.IsTrue(ans.Type == Sdk.ValueType.Text);
            Assert.IsFalse(ans.GetAnswered());
            Assert.IsFalse(ans.GetValue<TextValue>().IsAnswered);
            Assert.IsTrue(ans.GetValue<TextValue>().Type == Sdk.ValueType.Text);
            Assert.IsTrue(ans.GetValue<TextValue>().UserModifiable);

            // ensure lookup of name with incorrect casing fails
            Assert.IsFalse(anss.TryGetAnswer("author full name", Sdk.ValueType.Text, out ans));

            // check some repeated answer indexing rules
            Assert.IsTrue(anss.TryGetAnswer("Author Full Name", Sdk.ValueType.Text, out ans));
            Assert.IsTrue(ans.IsRepeated);
            Assert.IsTrue(ans.GetChildCount() == 1);
            Assert.IsTrue(ans.GetChildCount(0) == 1);
            Assert.IsTrue(ans.GetChildCount(1) == 0);
            Assert.IsTrue(ans.GetValue<TextValue>(0, 0).Value == "A");
            Assert.IsFalse(ans.GetValue<TextValue>(0, 1).IsAnswered);
            Assert.IsFalse(ans.GetValue<TextValue>(1).IsAnswered);
            Assert.IsFalse(ans.GetValue<TextValue>(1, 0).IsAnswered);
            // unusual HotDocs indexing rules
            Assert.IsTrue(ans.GetValue<TextValue>().IsAnswered);
            Assert.IsTrue(ans.GetValue<TextValue>().Value == "A");
            Assert.IsTrue(ans.GetValue<TextValue>(0).IsAnswered);
            Assert.IsTrue(ans.GetValue<TextValue>(0).Value == "A");
            Assert.IsTrue(ans.GetValue<TextValue>(0, 0, 0).IsAnswered);
            Assert.IsTrue(ans.GetValue<TextValue>(0, 0, 0).Value == "A");

            // ensure that lookup of non-existing answer fails
            Assert.IsFalse(anss.TryGetAnswer("does not exist", Sdk.ValueType.Text, out ans));
        }
        private static void LoadAnswerFileDataSource(AnswerFileDataSource answerFileDataSource)
        {
            Debug.Assert(s_readerWriterLock.IsWriteLockHeld);

            Stream answerFileStream = null;

            try
            {
                answerFileStream = new FileStream(answerFileDataSource.AnswerFilePath, FileMode.Open, FileAccess.Read);

                AnswerCollection answerSet = new AnswerCollection();
                answerSet.ReadXml(answerFileStream);

                ResourceSet   resourceSet  = s_metadata.ResourceSets.Single(rs => rs.Name == answerFileDataSource.ResourceId);
                ResourceType  resourceType = resourceSet.ResourceType;
                List <Answer> answers      = new List <Answer>(resourceType.Properties.Count);
                int           repeatCount  = 0;
                Answer        answer;
                foreach (var property in resourceType.Properties)
                {
                    if ((property.Kind & ResourcePropertyKind.Key) != ResourcePropertyKind.Key)
                    {
                        ValueType valueType = ValueType.Unknown;

                        // Infer from the CLR type what the ValueType is of the answer to be fetched.
                        if (property.ResourceType.InstanceType == typeof(string))
                        {
                            valueType = ValueType.Text;
                        }
                        else if (property.ResourceType.InstanceType == typeof(double?))
                        {
                            valueType = ValueType.Number;
                        }
                        else if (property.ResourceType.InstanceType == typeof(DateTime?))
                        {
                            valueType = ValueType.Date;
                        }
                        else if (property.ResourceType.InstanceType == typeof(bool?))
                        {
                            valueType = ValueType.TrueFalse;
                        }

                        if (valueType == ValueType.Unknown)
                        {
                            throw new Exception(string.Format("The metadata property '{0}' with a CLR type of '{1}' does not correspond to a supported " +
                                                              "HotDocs ValueType.", answerFileDataSource.PropertyNameToSourceNameMap[property.Name],
                                                              property.ResourceType.InstanceType.FullName));
                        }

                        if (answerSet.TryGetAnswer(answerFileDataSource.PropertyNameToSourceNameMap[property.Name], valueType, out answer) ||
                            ((valueType == ValueType.Text) &&
                             answerSet.TryGetAnswer(answerFileDataSource.PropertyNameToSourceNameMap[property.Name], ValueType.MultipleChoice, out answer)))
                        {
                            repeatCount = Math.Max(repeatCount, answer.GetChildCount());
                            answers.Add(answer);
                        }
                        else
                        {
                            answers.Add(null);
                        }
                    }
                }

                // Populate the data source with data.
                IList <DSPResource> resourceList = s_context.GetResourceSetEntities(resourceSet.Name);
                resourceList.Clear();

                for (int repeatIndex = 0; repeatIndex < repeatCount; repeatIndex++)
                {
                    var resource    = new DSPResource(resourceSet.ResourceType, s_readerWriterLock);
                    int answerIndex = 0;
                    for (int propertyIndex = 0; propertyIndex < resourceType.Properties.Count; propertyIndex++)
                    {
                        ResourceProperty property = resourceType.Properties[propertyIndex];
                        object           value;
                        if ((property.Kind & ResourcePropertyKind.Key) == ResourcePropertyKind.Key)
                        {
                            value = repeatIndex + 1;
                        }
                        else
                        {
                            IValue iValue = null;
                            answer = answers[answerIndex++];
                            if ((answer != null) && (repeatIndex <= answer.GetChildCount()))
                            {
                                iValue = answer.GetValue(repeatIndex);
                            }

                            if (property.ResourceType.InstanceType == typeof(string))
                            {
                                value = ((iValue != null) && iValue.IsAnswered) ? iValue.ToString(null) : string.Empty;
                            }
                            else if (property.ResourceType.InstanceType == typeof(double?))
                            {
                                if ((iValue != null) && iValue.IsAnswered)
                                {
                                    value = iValue.ToDouble(null);
                                }
                                else
                                {
                                    value = null;
                                }
                            }
                            else if (property.ResourceType.InstanceType == typeof(DateTime?))
                            {
                                if ((iValue != null) && iValue.IsAnswered)
                                {
                                    value = iValue.ToDateTime(null);
                                }
                                else
                                {
                                    value = null;
                                }
                            }
                            else
                            {
                                Debug.Assert(property.ResourceType.InstanceType == typeof(bool?));
                                if ((iValue != null) && iValue.IsAnswered)
                                {
                                    value = iValue.ToBoolean(null);
                                }
                                else
                                {
                                    value = null;
                                }
                            }
                        }

                        resource.SetValue(property.Name, value);
                    }

                    resourceList.Add(resource);
                }
            }
            catch (Exception e)
            {
                throw new DataServiceException(string.Format("Failed to read the answers for the data source key '{0}' from the answer file '{1}'.",
                                                             answerFileDataSource.DataSourceId, answerFileDataSource.DataSourceName), e);
            }
            finally
            {
                if (answerFileStream != null)
                {
                    answerFileStream.Close();
                }
            }
        }