/// <summary> /// /// </summary> /// <param name="initialData">Represents the current contents of the file. Reference is not held. A copy is made.</param> /// <param name="path"></param> /// <param name="data"></param> /// <param name="serializer"></param> private LocalizationFile(Id <FileInProject> id, MemoryStream initialData, DocumentPath path, LocalizerData data, ISerializer <LocalizerData> serializer, UpToDateFile.BackEnd backend) { Id = id; m_backend = backend; m_file = new SaveableFileExternalChangedSource(initialData, path.FileInfo, s => { serializer.Write(m_data, s); }, Changed, Saved, backend); m_data = data; }
public static void TestEverything() { using (UpToDateFile.BackEnd backEnd = new UpToDateFile.BackEnd()) { using (MemoryStream m = new MemoryStream()) { using (SaveableFileUndoable file = new SaveableFileUndoable(m, new FileInfo("ignore.txt"), a => { }, backEnd)) { Assert.False(file.Changed); file.Change(new GenericUndoAction(() => { }, () => { }, "")); Assert.True(file.Changed); file.UndoQueue.Undo(); Assert.False(file.Changed); file.UndoQueue.Redo(); Assert.True(file.Changed); file.Save(); Assert.False(file.Changed); file.Change(new GenericUndoAction(() => { }, () => { }, "")); Assert.True(file.Changed); file.UndoQueue.Undo(); Assert.False(file.Changed); file.UndoQueue.Undo(); Assert.True(file.Changed); file.UndoQueue.Redo(); Assert.False(file.Changed); } } } }
public static void StressTest() { MemoryStream data = new MemoryStream(); using (StreamWriter w = new StreamWriter(data, Encoding.UTF8, 1024, true)) { w.WriteLine("asgfladhfldsalkhdsfdsfasf"); } data.Position = 0; Action <Stream> saveto = s => { data.Position = 0; data.CopyTo(s); data.Position = 0; }; using (FileStream file = new FileStream("test.txt", FileMode.OpenOrCreate, FileAccess.Write)) { saveto(file); } using (UpToDateFile.BackEnd backEnd = new UpToDateFile.BackEnd()) { using (UpToDateFile f = new UpToDateFile(data, new FileInfo("test.txt"), saveto, backEnd)) { f.FileChanged += () => { Assert.Fail("FileChange should not have triggered"); }; try { for (int i = 0; i < 200; i++) { f.Save(); f.Save(); data.Position = 10; data.WriteByte((byte)(i % 256)); f.Save(); Console.WriteLine(i); } } catch (MyFileLoadException) { Assert.Fail(); } } } }
public ProjectMenuController(SharedContext context, ConfigParameterList <string> config, INodeFactory conversationNodeFactory, INodeFactory domainNodeFactory, Action <Action> executeInGuiThread, PluginsConfig pluginsConfig, Func <IAudioProviderCustomization> audioCustomization, UpToDateFile.BackEnd backend) { m_context = context; m_executeInGuiThread = executeInGuiThread; m_conversationNodeFactory = conversationNodeFactory; m_domainNodeFactory = domainNodeFactory; m_config = config; m_pluginsConfig = pluginsConfig; m_audioCustomization = audioCustomization; m_backend = backend; m_context.ProjectMoved += WeakCallback <Changed <FileInfo> > .Handler(this, (me, a) => me.ProjectMoved(a.From, a.To)); m_context.CurrentProject.Changed.Register(this, (a, b) => UpdateRecentlyOpenedConfig()); var file = m_config.Value.FirstOrDefault(a => true, a => a, ""); if (File.Exists(file)) { OpenProject(file); } }
public static void Test() { IEnumerable <GraphAndUI <NodeUIData> > nodes = Enumerable.Empty <GraphAndUI <NodeUIData> >(); List <NodeGroup> groups = new List <NodeGroup>(); MemoryStream rawData = new MemoryStream(); DocumentPath file = DocumentPath.FromPath("DeleteMe.txt", new DirectoryInfo(".")); ISerializer <TData> serializer = null; ReadOnlyCollection <LoadError> errors = new ReadOnlyCollection <LoadError>(new List <LoadError>()); INodeFactory nodeFactory = null; GenerateAudio generateAudio = null; var source = new DynamicEnumParameter.Source(); Func <IDynamicEnumParameter, object, DynamicEnumParameter.Source> getDocumentSource = (a, b) => source; IAudioLibrary audioProvider = new DummyAudioLibrary(); List <List <ConversationNode> > states = new List <List <ConversationNode <INodeGui> > > { new List <ConversationNode>() }; Random r = new Random(0); UpToDateFile.BackEnd backend = new UpToDateFile.BackEnd(); var id = Id <FileInProject> .Parse("6a1bd06a-0028-4099-a375-475f1a5320db"); using (ConversationFile conversationFile = new ConversationFile(id, nodes, groups, rawData, file, serializer, errors, nodeFactory, generateAudio, getDocumentSource, audioProvider, backend)) { for (int i = 0; i < 10; i++) { var node = MakeNode(); var state = states[i].ToList(); state.Add(node); conversationFile.Add(new[] { node }, Enumerable.Empty <NodeGroup>(), null); CheckState(conversationFile, state); states.Add(state); } Action <ConversationNode> CheckNode = node => { var connector = conversationFile.UIInfo(node.Data.Connectors.First(), false); Assert.That(connector.Area.Value, Is.EqualTo(TopPosition(node.Renderer.Area))); }; for (int n = 0; n < 10000; n++) { var node = states.Last().Last(); node.Renderer.MoveTo(new PointF((float)r.NextDouble() * 1000, (float)r.NextDouble() * 1000)); CheckNode(node); } for (int i = 9; i >= 0; i--) { conversationFile.UndoableFile.UndoQueue.Undo(); var state = states[i]; CheckState(conversationFile, state); } for (int i = 1; i <= 10; i++) { conversationFile.UndoableFile.UndoQueue.Redo(); var state = states[i]; CheckState(conversationFile, state); } } Assert.Inconclusive(); }
internal static ILocalizationFile Load(DocumentPath path, Id <FileInProject> id, ISerializer <LocalizerData> serializer, UpToDateFile.BackEnd backend) { if (path.Exists) { try { LocalizerData data; using (FileStream file = Util.LoadFileStream(path.AbsolutePath, FileMode.Open, FileAccess.Read)) { using (MemoryStream m = new MemoryStream((int)file.Length)) { file.CopyTo(m); m.Position = 0; XmlLocalization.Deserializer d = new XmlLocalization.Deserializer(); data = d.Read(m); m.Position = 0; LocalizationFile result = new LocalizationFile(id, m, path, data, serializer, backend); return(result); } } } catch (MyFileLoadException e) { Console.Out.WriteLine(e.Message); Console.Out.WriteLine(e.StackTrace); Console.Out.WriteLine(e.InnerException.Message); Console.Out.WriteLine(e.InnerException.StackTrace); MessageBox.Show("File: " + path.AbsolutePath + " could not be accessed"); return(new MissingLocalizationFile(id, path)); } } else { return(new MissingLocalizationFile(id, path)); } }
internal static LocalizationFile MakeNew(DirectoryInfo directory, Func <string, Id <FileInProject>, ISerializer <LocalizerData> > serializer, Func <FileInfo, bool> pathOk, UpToDateFile.BackEnd backend, DirectoryInfo origin) { //Create a stream under an available filename FileInfo path = null; for (int i = 0; path == null; i++) { path = new FileInfo(directory.FullName + Path.DirectorySeparatorChar + "New Localization " + i + ".loc"); if (!pathOk(path)) { path = null; } } LocalizerData data = new LocalizerData(); using (var file = Util.LoadFileStream(path, FileMode.CreateNew, FileAccess.Write)) { } using (var mem = new MemoryStream()) { var id = Id <FileInProject> .New(); LocalizationFile result = new LocalizationFile(id, mem, DocumentPath.FromPath(path, origin), data, serializer(path.FullName, id), backend); //Make a new localization file for an existing project result.File.Writable.Save(); return(result); } }
/// <param name="context">Context used when localizing to reference current localization</param> /// <param name="usedGuids"></param> /// <param name="shouldClean"></param> /// <param name="shouldExpand"></param> /// <param name="pathOk">Path is an acceptable filename for a new localization file</param> /// <param name="fileLocationOk">Path is an acceptable location from which to import an existing localization file</param> public LocalizationEngine(GetFilePath getFilePath, IEnumerable <Project.TData.LocalizerSetData> sets, ILocalizationContext context, Func <HashSet <Id <LocalizedText> > > usedGuids, Func <string, bool> shouldClean, Func <FileInfo, bool> pathOk, Func <string, bool> fileLocationOk, UpToDateFile.BackEnd backEnd, DirectoryInfo origin) { m_context = context; m_usedGuids = usedGuids; ShouldClean = shouldClean; Func <IEnumerable <Tuple <Id <FileInProject>, DocumentPath> >, IEnumerable <ILocalizationFile> > load = files => { //return files.Select(file => LocalizationFile.Load(file, MakeSerializer(file.Name), backend)); var filesAndSerializer = files.Select(f => new { Id = f.Item1, Path = f.Item2, Serializer = MakeSerializer(f.Item2.AbsolutePath, f.Item1) }).ToList(); return(ParallelEnumerable.Select(filesAndSerializer.AsParallel(), fs => LocalizationFile.Load(fs.Path, fs.Id, fs.Serializer, backEnd))); }; Func <DirectoryInfo, LocalizationFile> makeEmpty = path => LocalizationFile.MakeNew(path, MakeSerializer, pathOk, backEnd, origin); m_localizers = new ProjectElementList <ILocalizationFile>(getFilePath, fileLocationOk.Bottleneck(), load, makeEmpty); m_localizationSets = sets.ToHashSet(); }
public static ConversationFile CreateEmpty(DirectoryInfo directory, Project project, INodeFactory nodeFactory, GenerateAudio generateAudio, Func <IDynamicEnumParameter, object, DynamicEnumParameter.Source> getDocumentSource, IAudioLibrary audioProvider, UpToDateFile.BackEnd backEnd, DirectoryInfo origin) { var file = GetAvailableConversationPath(directory, project.Elements); var nodes = Enumerable.Empty <GraphAndUI <NodeUIData> >(); var groups = new List <NodeGroup>(); //Fill the stream with the essential content using (MemoryStream m = new MemoryStream()) { using (FileStream stream = Util.LoadFileStream(file, FileMode.CreateNew, FileAccess.Write)) { project.ConversationSerializer.Write(SerializationUtils.MakeConversationData(nodes, new ConversationEditorData(groups)), m); m.Position = 0; m.CopyTo(stream); } var result = new ConversationFile(Id <FileInProject> .New(), nodes, groups, m, DocumentPath.FromPath(file, origin), project.ConversationSerializer, new ReadOnlyCollection <LoadError>(new LoadError[0]), nodeFactory, generateAudio, getDocumentSource, audioProvider, backEnd); result.m_file.Save(); //Make sure the file starts life as a valid xml document return(result); } }
/// <summary> /// /// </summary> /// <param name="nodes"></param> /// <param name="groups"></param> /// <param name="rawData">Represents the current contents of the file. Reference is not held. A copy is made.</param> /// <param name="file"></param> /// <param name="serializer"></param> /// <param name="errors"></param> /// <param name="nodeFactory"></param> /// <param name="generateAudio"></param> /// <param name="getDocumentSource"></param> /// <param name="audioProvider"></param> public ConversationFile(Id <FileInProject> id, IEnumerable <GraphAndUI <NodeUIData> > nodes, IEnumerable <NodeGroup> groups, MemoryStream rawData, DocumentPath file, ISerializer <TData> serializer, ReadOnlyCollection <LoadError> errors, INodeFactory nodeFactory, GenerateAudio generateAudio, Func <IDynamicEnumParameter, object, DynamicEnumParameter.Source> getDocumentSource, IAudioLibrary audioProvider, UpToDateFile.BackEnd backEnd) : base(nodes, groups, errors, nodeFactory, generateAudio, getDocumentSource, audioProvider) { Id = id; m_file = new SaveableFileUndoable(rawData, file.FileInfo, SaveTo, backEnd); m_serializer = serializer; foreach (var node in m_nodes) { //TODO: AUDIO: Why are we automatically decorrupting specifically audio parameters? var audios = node.Data.Parameters.OfType <IAudioParameter>(); foreach (var aud in audios) { if (aud.Corrupted) { var val = generateAudio(this); aud.SetValueAction(val).Value.Redo(); m_file.ChangeNoUndo(); audioProvider.UpdateUsage(val); } } node.UpdateRendererCorruption(); } }
public static IConversationFile Load(Id <FileInProject> file, DocumentPath path, INodeFactory nodeFactory, ISerializerDeserializer <TData> serializer, GenerateAudio generateAudio, Func <IDynamicEnumParameter, object, DynamicEnumParameter.Source> getDocumentSource, IAudioLibrary audioProvider, UpToDateFile.BackEnd backEnd) { try { using (var stream = Util.LoadFileStream(path.FileInfo, FileMode.Open, FileAccess.Read)) { using (MemoryStream m = new MemoryStream((int)stream.Length)) { stream.CopyTo(m); stream.Dispose(); m.Position = 0; TData data = serializer.Read(m); return(new ConversationFile(file, data.Nodes.ToList(), data.EditorData.Groups.ToList(), m, path, serializer, data.Errors, nodeFactory, generateAudio, getDocumentSource, audioProvider, backEnd)); } } } catch (MyFileLoadException e) { Console.Out.WriteLine(e.Message); Console.Out.WriteLine(e.StackTrace); Console.Out.WriteLine(e.InnerException.Message); Console.Out.WriteLine(e.InnerException.StackTrace); MessageBox.Show("File: " + path.AbsolutePath + " could not be accessed"); return(new MissingConversationFile(file, path)); } catch (DeserializerVersionMismatchException e) { MessageBox.Show("File: " + path.AbsolutePath + " could not be processed. " + e.Message); return(new MissingConversationFile(file, path)); } }
/// <summary> /// /// </summary> /// <param name="context"></param> /// <param name="data"></param> /// <param name="conversationNodeFactory"></param> /// <param name="domainNodeFactory"></param> /// <param name="initialContent">Represents the current contents of the file. Reference is not held. A copy is made.</param> /// <param name="projectFile"></param> /// <param name="serializer"></param> /// <param name="conversationSerializer"></param> /// <param name="conversationSerializerDeserializerFactory"></param> /// <param name="domainSerializer"></param> /// <param name="pluginsConfig"></param> /// <param name="audioCustomization"></param> public Project(ILocalizationContext context, TData data, INodeFactory conversationNodeFactory, INodeFactory domainNodeFactory, MemoryStream initialData, FileInfo projectFile, ISerializer <TData> serializer, ISerializer <TConversationData> conversationSerializer, ConversationSerializerDeserializerFactory conversationSerializerDeserializerFactory, ISerializer <TDomainData> domainSerializer, PluginsConfig pluginsConfig, Func <IAudioProviderCustomization> audioCustomization, UpToDateFile.BackEnd backend) { AssertUniqueFileIds(data); AssertUniqueLocalizationSetNames(data); AssertUniqueFilePaths(data); m_upToDateFileBackend = backend; Action <Stream> saveTo = stream => { Write(GetFilePath, Conversations.Select(x => (x.Id)), LocalizationFiles.Select(x => (x.Id)), DomainFiles.Select(x => (x.Id)), AudioFiles.Select(x => (x.Id)), Localizer.LocalizationSets, stream, m_serializer); }; m_file = new SaveableFileNotUndoable(initialData, projectFile, saveTo, backend); ConversationNodeFactory = conversationNodeFactory; DomainNodeFactory = domainNodeFactory; m_serializer = serializer; ConversationSerializer = conversationSerializer; m_conversationSerializerFactory = conversationSerializerDeserializerFactory; m_domainSerializer = domainSerializer; m_filePaths = data.Conversations.Concat(data.Localizations).Concat(data.Domains).Concat(data.Audios).ToDictionary(f => f.Id, f => f.Path); IEnumerable <Id <FileInProject> > conversationIds = data.Conversations.Select(f => f.Id); IEnumerable <Id <FileInProject> > localizerIds = data.Localizations.Select(f => f.Id); IEnumerable <Id <FileInProject> > domainIds = data.Domains.Select(f => f.Id); IEnumerable <Id <FileInProject> > audioIds = data.Audios.Select(f => f.Id); m_audioProvider = new AudioProvider(new FileInfo(projectFile.FullName), GetFilePath, s => CheckFolder(s, Origin), this, audioCustomization()); using (m_audioProvider.SuppressUpdates()) { { m_audioProvider.AudioFiles.Load(audioIds); } { //Dictionary<IDynamicEnumParameter, DynamicEnumParameter.Source> domainEnumSource = new Dictionary<IDynamicEnumParameter, DynamicEnumParameter.Source>(); Func <IDynamicEnumParameter, object, DynamicEnumParameter.Source> getDomainEnumSource = (k, o) => { return(null); //Nothing should need a source (but the system will ask anyway) //if (!domainEnumSource.ContainsKey(k)) //domainEnumSource[k] = new DynamicEnumParameter.Source(); //return domainEnumSource[k]; }; m_domainDataSource = new DomainDomain(pluginsConfig); Func <IEnumerable <Tuple <Id <FileInProject>, DocumentPath> >, IEnumerable <IDomainFile> > loader = paths => { var result = DomainFile.Load(paths, m_domainDataSource, DomainSerializerDeserializer.Make, DomainNodeFactory, () => DomainUsage, getDomainEnumSource, backend).Evaluate(); result.ForAll(a => a.ConversationDomainModified += ConversationDatasourceModified); return(result); }; Func <DirectoryInfo, DomainFile> makeEmpty = path => DomainFile.CreateEmpty(path, m_domainDataSource, m_domainSerializer, pathOk, DomainNodeFactory, () => DomainUsage, getDomainEnumSource, backend, Origin); m_domainFiles = new ProjectElementList <IDomainFile>(GetFilePath, s => CheckFolder(s, Origin), loader, makeEmpty); m_domainFiles.Load(domainIds); } m_conversationDataSource = new ConversationDataSource(m_domainFiles.Select(df => df.Data)); if (m_conversationDataSource.DomainErrors.Any()) { MessageBox.Show(string.Join("\n", m_conversationDataSource.DomainErrors.Select(error => error.Message))); } { m_localizer = new LocalizationEngine(GetFilePath, data.LocalizationSets, context, UsedLocalizations, ShouldContract, pathOk, s => CheckFolder(s, Origin), backend, Origin); m_localizer.Localizers.Load(localizerIds); context.CurrentLocalization.Value = m_localizer.LocalizationSets.FirstOrDefault() ?? Project.TData.LocalizerSetData.Empty; m_localizer.LocalizationSetsChanged += () => { m_file.Change(); }; } { GenerateAudio audio = (c) => { return(m_audioProvider.Generate(new AudioGenerationParameters(c.File.File, this.File.File))); }; //This can be called from multiple threads simultaneously and in arbitrary orders by design of //ConversationDataSource and the underlying ConstantTypeSet and DynamicEnumParameter.Source Func <IDynamicEnumParameter, object, DynamicEnumParameter.Source> getSource = (localEnum, newSourceID) => { return(m_conversationDataSource.GetSource(localEnum.TypeId, newSourceID)); }; Func <IEnumerable <Tuple <Id <FileInProject>, DocumentPath> >, IEnumerable <IConversationFile> > loadConversations = files => { ISerializerDeserializer <XmlGraphData <NodeUIData, ConversationEditorData> > conversationSerializerDeserializer = m_conversationSerializerFactory(m_conversationDataSource); // _/ _/ _/ X _/ X _/ //return files.Select(file => ConversationFile.Load(file, ConversationNodeFactory, conversationSerializerDeserializer, audio, getSource, m_audioProvider, backend)); //TODO: AUDIO: This is ok as long as we're not using audio parameters at all return(ParallelEnumerable.Select(files.AsParallel(), file => ConversationFile.Load(file.Item1, file.Item2, ConversationNodeFactory, conversationSerializerDeserializer, audio, getSource, m_audioProvider, backend))); }; Func <DirectoryInfo, ConversationFile> makeEmpty = path => ConversationFile.CreateEmpty(path, this, ConversationNodeFactory, audio, getSource, m_audioProvider, backend, Origin); m_conversations = new ProjectElementList <IConversationFile>(GetFilePath, s => CheckFolder(s, Origin), loadConversations, makeEmpty); m_conversations.Load(conversationIds); } RefreshCallbacks(m_conversations, m_actionsOnRefreshCallbacksConversations); RefreshCallbacks(m_audioProvider.AudioFiles, m_actionsOnRefreshCallbacksAudios); RefreshCallbacks(m_localizer.Localizers, m_actionsOnRefreshCallbacksLocalizations); RefreshCallbacks(m_domainFiles, m_actionsOnRefreshCallbacksDomains); m_conversations.GotChanged += GotChanged; m_audioProvider.AudioFiles.GotChanged += GotChanged; m_localizer.Localizers.GotChanged += GotChanged; m_domainFiles.GotChanged += GotChanged; m_conversations.GotChanged += () => RefreshCallbacks(m_conversations, m_actionsOnRefreshCallbacksConversations); m_audioProvider.AudioFiles.GotChanged += () => RefreshCallbacks(m_audioProvider.AudioFiles, m_actionsOnRefreshCallbacksAudios); m_localizer.Localizers.GotChanged += () => RefreshCallbacks(m_localizer.Localizers, m_actionsOnRefreshCallbacksLocalizations); m_domainFiles.GotChanged += () => RefreshCallbacks(m_domainFiles, m_actionsOnRefreshCallbacksDomains); m_domainFiles.GotChanged += ConversationDatasourceModified; Action <IDomainFile> MaybeConversationDatasourceModified = file => { if (!file.File.Changed()) { ConversationDatasourceModified(); } }; m_domainFiles.Added += argument => { argument.File.SaveStateChanged += () => MaybeConversationDatasourceModified(argument); }; m_domainFiles.Reloaded += (_, argument) => { argument.File.SaveStateChanged += () => MaybeConversationDatasourceModified(argument); }; m_domainFiles.ForAll(d => d.File.SaveStateChanged += () => MaybeConversationDatasourceModified(d)); //m_domainFiles.Added += argument => { argument.File.SaveStateChanged += () => { if (!argument.File.Changed) ReloadDatasource(); }; }; //m_domainFiles.Reloaded += (from, to) => { to.File.SaveStateChanged += () => { if (!to.File.Changed) ReloadDatasource(); }; }; //m_domainFiles.ForAll(d => d.File.SaveStateChanged += () => { if (!d.File.Changed) ReloadDatasource(); }); m_domainFiles.Added += file => m_filePaths[file.Id] = DocumentPath.FromAbsolutePath(file.File.File.FullName, Origin); m_conversations.Added += file => m_filePaths[file.Id] = DocumentPath.FromAbsolutePath(file.File.File.FullName, Origin); m_audioProvider.AudioFiles.Added += file => m_filePaths[file.Id] = DocumentPath.FromAbsolutePath(file.File.File.FullName, Origin); m_localizer.Localizers.Added += file => m_filePaths[file.Id] = DocumentPath.FromAbsolutePath(file.File.File.FullName, Origin); m_domainFiles.Removed += file => m_filePaths.Remove(file.Id); m_conversations.Removed += file => m_filePaths.Remove(file.Id); m_audioProvider.AudioFiles.Removed += file => m_filePaths.Remove(file.Id); m_localizer.Localizers.Removed += file => m_filePaths.Remove(file.Id); //Files being moved is setup in RefreshCallbacks() DomainUsage = new DomainUsage(this); m_audioProvider.UpdateUsage(); } }
public static Project CreateEmpty(ILocalizationContext context, FileInfo path, INodeFactory conversationNodeFactory, INodeFactory domainNodeFactory, ISerializer <TData> serializer, ISerializer <TConversationData> conversationSerializer, ConversationSerializerDeserializerFactory conversationSerializerDeserializer, ISerializer <TDomainData> domainSerializer, PluginsConfig pluginsConfig, Func <IAudioProviderCustomization> audioCustomization, UpToDateFile.BackEnd backEnd) { using (MemoryStream m = new MemoryStream()) { //Create the new conversation file stream, fill with essential content and close FileInfo conversationFile = ConversationFile.GetAvailableConversationPath(path.Directory, Enumerable.Empty <ISaveableFileProvider>()); using (FileStream conversationStream = Util.LoadFileStream(conversationFile, FileMode.CreateNew, FileAccess.Write)) { conversationSerializer.Write(SerializationUtils.MakeConversationData(Enumerable.Empty <ConversationNode>(), new ConversationEditorData()), conversationStream); } //Create the new project GetFilePath getFilePath = null; //Should never need this as all the FileId lists are empty Write(getFilePath, Enumerable.Empty <Id <FileInProject> >(), Enumerable.Empty <Id <FileInProject> >(), Enumerable.Empty <Id <FileInProject> >(), Enumerable.Empty <Id <FileInProject> >(), Enumerable.Empty <TData.LocalizerSetData>(), m, serializer); using (FileStream projectfile = Util.LoadFileStream(path, FileMode.Create, FileAccess.Write)) { m.Position = 0; m.CopyTo(projectfile); m.Position = 0; } TData data = new TData(Enumerable.Empty <TData.FileIdAndPath>(), Enumerable.Empty <TData.FileIdAndPath>(), Enumerable.Empty <TData.FileIdAndPath>(), Enumerable.Empty <TData.FileIdAndPath>(), Enumerable.Empty <TData.LocalizerSetData>()); Project result = new Project(context, data, conversationNodeFactory, domainNodeFactory, m, path, serializer, conversationSerializer, conversationSerializerDeserializer, domainSerializer, pluginsConfig, audioCustomization, backEnd); return(result); } }
/// <summary> /// /// </summary> /// <param name="nodes"></param> /// <param name="groups"></param> /// <param name="rawData">Represents the current contents of the file. Reference is not held. A copy is made.</param> /// <param name="file"></param> /// <param name="errors"></param> /// <param name="datasource"></param> /// <param name="serializer"></param> /// <param name="nodeFactory"></param> /// <param name="domainUsage"></param> /// <param name="getDocumentSource"></param> /// <param name="autoCompletePatterns"></param> public DomainFile(IEnumerable <GraphAndUI <NodeUIData> > nodes, IEnumerable <NodeGroup> groups, MemoryStream rawData, Id <FileInProject> file, DocumentPath path, ReadOnlyCollection <LoadError> errors, DomainDomain datasource, ISerializer <TData> serializer, INodeFactory nodeFactory, Func <IDomainUsage <ConversationNode, TransitionNoduleUIInfo> > domainUsage, Func <IDynamicEnumParameter, object, DynamicEnumParameter.Source> getDocumentSource, IEnumerable <IAutoCompletePattern> autoCompletePatterns, UpToDateFile.BackEnd backEnd) : base(nodes, groups, errors, nodeFactory, null, getDocumentSource, NoAudio.Instance) { Id = file; m_file = new SaveableFileUndoable(rawData, path.FileInfo, SaveTo, backEnd); m_domainUsage = domainUsage; foreach (var node in m_nodes) { var n = node; node.Modified += () => NodeModified(n); node.Data.Linked += () => NodeLinked(n); } m_nodes.Inserting += (n) => { AddToData(n.Data.Only(), m_datasource); ConversationDomainModified.Execute(); //No need to be picky about false positives n.Modified += () => NodeModified(n); n.Data.Linked += () => NodeLinked(n); }; m_nodes.Removing += RemoveFromData; m_nodes.Clearing += ClearData; //Currently nothing clears the list m_datasource = datasource; //m_conversationDatasource = conversationDataSource; m_serializer = serializer; m_autoCompletePatterns = new List <IAutoCompletePattern>(autoCompletePatterns); }
internal static IEnumerable <IDomainFile> Load(IEnumerable <Tuple <Id <FileInProject>, DocumentPath> > paths, DomainDomain source, Func <DomainDomain, DomainSerializerDeserializer> serializerdeserializer, INodeFactory nodeFactory, Func <IDomainUsage <ConversationNode, TransitionNoduleUIInfo> > domainUsage, Func <IDynamicEnumParameter, object, DynamicEnumParameter.Source> getDocumentSource, UpToDateFile.BackEnd backend) { var streamsAndPaths = paths.Select((x) => { var fileId = x.Item1; DocumentPath path = x.Item2; try { using (var stream = Util.LoadFileStream(path.FileInfo, FileMode.Open, FileAccess.Read)) { return((Either <Tuple <MemoryStream, Id <FileInProject>, DocumentPath>, MissingDomainFile>)Tuple.Create(StreamUtil.Copy(stream), fileId, path)); } } catch (MyFileLoadException e) { Console.Out.WriteLine(e.Message); Console.Out.WriteLine(e.StackTrace); Console.Out.WriteLine(e.InnerException.Message); Console.Out.WriteLine(e.InnerException.StackTrace); if (path.Exists) { MessageBox.Show("File: " + path.AbsolutePath + " could not be accessed"); } else { MessageBox.Show("File: " + path.AbsolutePath + " does not exist"); } MissingDomainFile temp = null; try { temp = new MissingDomainFile(fileId, path); Either <Tuple <MemoryStream, Id <FileInProject>, DocumentPath>, MissingDomainFile> result = temp; return(result); } catch { temp?.Dispose(); throw; } } }).Evaluate(); var validStreamsAndPaths = Either.Split(streamsAndPaths).Item1; //TODO: Use this in the below cases where we currently use streamsAndPaths IReadOnlyCollection <Guid> nonUniqueGuids = serializerdeserializer(source).CheckUniqueIds(validStreamsAndPaths.Select(x => x.Item1)); if (nonUniqueGuids.Any()) { //TODO: Provide feedback to the user as to why these failed to load. //TODO: Unit test this return(streamsAndPaths.Select(x => x.Transformed(a => new MissingDomainFile(a.Item2, a.Item3), a => a))); } //We make the, hopefully, valid assumption here that the deserializers for the various concepts within a domain file have the same version requirements. Dictionary <DocumentPath, DeserializerVersionMismatchException> failedToParseFiles = new Dictionary <DocumentPath, DeserializerVersionMismatchException>(); foreach (var sp in streamsAndPaths) { sp.Do(stream => { try { var categoryData = serializerdeserializer(source).CategoriesDeserializer.Read(stream.Item1); DomainFile.AddToData(categoryData.Nodes.Select(n => n.GraphData), source); } catch (DeserializerVersionMismatchException e) { failedToParseFiles[stream.Item3] = e; } }, a => { }); } foreach (var sp in streamsAndPaths) { sp.Do(stream => { try { var typeData = serializerdeserializer(source).TypesDeserializer.Read(stream.Item1); DomainFile.AddToData(typeData.Nodes.Select(n => n.GraphData), source); } catch (DeserializerVersionMismatchException e) { failedToParseFiles[stream.Item3] = e; } }, a => { }); } foreach (var sp in streamsAndPaths) { sp.Do(stream => { try { var connectorData = serializerdeserializer(source).ConnectorsDeserializer.Read(stream.Item1); DomainFile.AddToData(connectorData.Nodes.Select(n => n.GraphData), source); } catch (DeserializerVersionMismatchException e) { failedToParseFiles[stream.Item3] = e; } }, a => { }); } foreach (var sp in streamsAndPaths) { sp.Do(stream => { try { var nodeData = serializerdeserializer(source).NodesDeserializer.Read(stream.Item1); DomainFile.AddToData(nodeData.Nodes.Select(n => n.GraphData), source); } catch (DeserializerVersionMismatchException e) { failedToParseFiles[stream.Item3] = e; } }, a => { }); } { var result = streamsAndPaths.Select(a => a.Transformed <IDomainFile>(stream => { try { var editorData = serializerdeserializer(source).EditorDataDeserializer.Read(stream.Item1); DomainFile.AddToData(editorData.Nodes.Select(n => n.GraphData), source); var allData = serializerdeserializer(source).EverythingDeserializer.Read(stream.Item1); List <IAutoCompletePattern> autoCompletePatterns = new List <IAutoCompletePattern>(); var nodeData = serializerdeserializer(source).AutoCompleteSuggestionsDeserializer.Read(stream.Item1); autoCompletePatterns.AddRange(AutoCompletePattern.Generate(nodeData, source)); return(new DomainFile(allData.Nodes.ToList(), editorData.EditorData.Groups.ToList(), stream.Item1, stream.Item2, stream.Item3, allData.Errors, source, serializerdeserializer(source).Serializer, nodeFactory, domainUsage, getDocumentSource, autoCompletePatterns, backend)); } catch (DeserializerVersionMismatchException e) { failedToParseFiles[stream.Item3] = e; return(new MissingDomainFile(stream.Item2, stream.Item3)); } }, b => b)); if (failedToParseFiles.Any()) { //TODO: expose this to the user in a consistent way with other errors (missing files, duplicated or missing guids) StringBuilder message = new StringBuilder("Failed to parse files:\n"); foreach (var kvp in failedToParseFiles) { message.Append("\n"); message.Append(kvp.Key.RelativePath); message.Append(": "); message.Append(kvp.Value.Message); } MessageBox.Show(message.ToString()); } return(result); } }
public static DomainFile CreateEmpty(DirectoryInfo directory, DomainDomain datasource, ISerializer <TData> serializer, Func <FileInfo, bool> pathOk, INodeFactory nodeFactory, Func <IDomainUsage <ConversationNode, TransitionNoduleUIInfo> > domainUsage, Func <IDynamicEnumParameter, object, DynamicEnumParameter.Source> getDocumentSource, UpToDateFile.BackEnd backEnd, DirectoryInfo origin) { //Create a stream under an available filename FileInfo path = null; for (int i = 0; path == null; i++) { path = new FileInfo(directory.FullName + Path.DirectorySeparatorChar + "New Domain " + i + ".dom"); if (!pathOk(path)) { path = null; } } using (MemoryStream m = new MemoryStream()) { using (var stream = Util.LoadFileStream(path, FileMode.CreateNew, FileAccess.Write)) { serializer.Write(SerializationUtils.MakeDomainData(Enumerable.Empty <ConversationNode>(), new ConversationEditorData()), m); m.CopyTo(stream); } var result = new DomainFile(new List <GraphAndUI <NodeUIData> >(), new List <NodeGroup>(), m, Id <FileInProject> .New(), DocumentPath.FromPath(path, origin), new ReadOnlyCollection <LoadError>(new LoadError[0]), datasource, serializer, nodeFactory, domainUsage, getDocumentSource, new List <IAutoCompletePattern>(), backEnd); result.m_file.Save(); //Make sure the file starts life as a valid xml document return(result); } }