Browse Source

New OpenedFile API based on file models.

filemodels
Daniel Grunwald 12 years ago
parent
commit
866f0d57aa
  1. 16
      src/AddIns/DisplayBindings/AvalonEdit.AddIn/Src/Utils.cs
  2. 5
      src/Main/Base/Project/ICSharpCode.SharpDevelop.csproj
  3. 9
      src/Main/Base/Project/Services/IFileSystem.cs
  4. 10
      src/Main/Base/Project/Util/SharpDevelopExtensions.cs
  5. 72
      src/Main/Base/Project/Workbench/File/BinaryFileModelProvider.cs
  6. 29
      src/Main/Base/Project/Workbench/File/DocumentFileModelInfo.cs
  7. 38
      src/Main/Base/Project/Workbench/File/FileModels.cs
  8. 76
      src/Main/Base/Project/Workbench/File/IFileModelProvider.cs
  9. 181
      src/Main/Base/Project/Workbench/File/OpenedFile.cs
  10. 89
      src/Main/Base/Project/Workbench/File/TextDocumentFileModelProvider.cs
  11. 9
      src/Main/Core/Project/Src/Services/FileUtility/PathName.cs

16
src/AddIns/DisplayBindings/AvalonEdit.AddIn/Src/Utils.cs

@ -28,22 +28,6 @@ namespace ICSharpCode.AvalonEdit.AddIn @@ -28,22 +28,6 @@ namespace ICSharpCode.AvalonEdit.AddIn
{
public static class Utils
{
public static OffsetChangeMap ToOffsetChangeMap(this IEnumerable<Edit> edits)
{
var map = new OffsetChangeMap();
int diff = 0;
foreach (var edit in edits) {
Debug.Assert(edit.EditType != ChangeType.None && edit.EditType != ChangeType.Unsaved);
int offset = edit.BeginA + diff;
int removalLength = edit.EndA - edit.BeginA;
int insertionLength = edit.EndB - edit.BeginB;
diff += (insertionLength - removalLength);
map.Add(new OffsetChangeMapEntry(offset, removalLength, insertionLength));
}
return map;
}
/// <summary>
/// Copies editor options and default element customizations.
/// Does not copy the syntax highlighting.

5
src/Main/Base/Project/ICSharpCode.SharpDevelop.csproj

@ -361,8 +361,13 @@ @@ -361,8 +361,13 @@
<Compile Include="Workbench\DisplayBinding\ISecondaryDisplayBinding.cs" />
<Compile Include="Workbench\FakeXmlViewContent.cs" />
<Compile Include="Workbench\FileChangeWatcher.cs" />
<Compile Include="Workbench\File\BinaryFileModelProvider.cs" />
<Compile Include="Workbench\File\DocumentFileModelInfo.cs" />
<Compile Include="Workbench\File\FileModels.cs" />
<Compile Include="Workbench\File\FileService.cs" />
<Compile Include="Workbench\File\IFileModelProvider.cs" />
<Compile Include="Workbench\File\IRecentOpen.cs" />
<Compile Include="Workbench\File\TextDocumentFileModelProvider.cs" />
<Compile Include="Workbench\ICustomizedCommands.cs" />
<Compile Include="Workbench\IOutputPad.cs" />
<Compile Include="Workbench\IPadContent.cs" />

9
src/Main/Base/Project/Services/IFileSystem.cs

@ -34,10 +34,10 @@ namespace ICSharpCode.SharpDevelop @@ -34,10 +34,10 @@ namespace ICSharpCode.SharpDevelop
/// <inheritdoc cref="System.IO.File.Delete"/>
void Delete(FileName path);
/// <inheritdoc cref="System.IO.File.CopyFile"/>
/// <inheritdoc cref="System.IO.File.Copy(string, string, bool)"/>
void CopyFile(FileName source, FileName destination, bool overwrite = false);
/// <inheritdoc cref="System.IO.Directory.Delete"/>
/// <inheritdoc cref="System.IO.Directory.CreateDirectory"/>
void CreateDirectory(DirectoryName path);
/// <inheritdoc cref="System.IO.File.OpenWrite"/>
@ -69,10 +69,13 @@ namespace ICSharpCode.SharpDevelop @@ -69,10 +69,13 @@ namespace ICSharpCode.SharpDevelop
/// Options that influence the search.
/// </param>
/// <returns>An enumerable that iterates through the directory contents</returns>
/// <exception cref="IOExcption">The directory does not exist / access is denied.</exception>
/// <exception cref="IOException">The directory does not exist / access is denied.</exception>
IEnumerable<FileName> GetFiles(DirectoryName directory, string searchPattern = "*", DirectorySearchOptions searchOptions = DirectorySearchOptions.None);
}
/// <summary>
/// Options for <c>SD.FileSystem.GetFiles()</c>.
/// </summary>
[Flags]
public enum DirectorySearchOptions
{

10
src/Main/Base/Project/Util/SharpDevelopExtensions.cs

@ -28,6 +28,7 @@ using System.Windows.Controls; @@ -28,6 +28,7 @@ using System.Windows.Controls;
using System.Windows.Media;
using System.Xml;
using System.Xml.Linq;
using ICSharpCode.AvalonEdit.Document;
using ICSharpCode.Core;
using ICSharpCode.Core.Presentation;
using ICSharpCode.NRefactory;
@ -36,6 +37,7 @@ using ICSharpCode.NRefactory.TypeSystem; @@ -36,6 +37,7 @@ using ICSharpCode.NRefactory.TypeSystem;
using ICSharpCode.SharpDevelop.Dom;
using ICSharpCode.SharpDevelop.Parser;
using ICSharpCode.SharpDevelop.Project;
using ICSharpCode.SharpDevelop.Workbench;
namespace ICSharpCode.SharpDevelop
{
@ -1028,6 +1030,14 @@ namespace ICSharpCode.SharpDevelop @@ -1028,6 +1030,14 @@ namespace ICSharpCode.SharpDevelop
return document.GetText(startOffset, document.GetOffset(endPos) - startOffset);
}
/// <summary>
/// Retrieves the DocumentFileModelInfo associated with this TextDocument.
/// </summary>
public static DocumentFileModelInfo GetFileModelInfo(this TextDocument document)
{
return document.GetService<DocumentFileModelInfo>();
}
/// <summary>
/// Obsolete. Use GetOffset() instead.
/// </summary>

72
src/Main/Base/Project/Workbench/File/BinaryFileModelProvider.cs

@ -0,0 +1,72 @@ @@ -0,0 +1,72 @@
// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt)
// This code is distributed under the GNU LGPL (for details please see \doc\license.txt)
using System;
using System.IO;
using ICSharpCode.Core;
namespace ICSharpCode.SharpDevelop.Workbench
{
sealed class BinaryFileModelProvider : IFileModelProvider<IBinaryModel>
{
sealed class OnDiskBinaryModel : IBinaryModel
{
internal readonly OpenedFile file;
public OnDiskBinaryModel(OpenedFile file)
{
this.file = file;
}
public Stream OpenRead()
{
return SD.FileSystem.OpenRead(file.FileName);
}
}
public IBinaryModel Load(OpenedFile file)
{
return new OnDiskBinaryModel(file);
}
public void Save(OpenedFile file, IBinaryModel model)
{
SaveCopyAs(file, model, file.FileName);
file.ReplaceModel(this, model, ReplaceModelMode.SetAsValid); // remove dirty flag
}
public void SaveCopyAs(OpenedFile file, IBinaryModel model, FileName outputFileName)
{
var onDisk = model as OnDiskBinaryModel;
if (onDisk != null) {
// We can just copy the file (but avoid copying to itself)
if (onDisk.file.FileName != outputFileName) {
SD.FileSystem.CopyFile(onDisk.file.FileName, outputFileName);
}
} else {
using (var inputStream = model.OpenRead()) {
using (var outputStream = SD.FileSystem.OpenWrite(outputFileName)) {
inputStream.CopyTo(outputStream);
}
}
}
}
public bool CanLoadFrom<U>(IFileModelProvider<U> otherProvider) where U : class
{
return false;
}
public void NotifyRename(OpenedFile file, IBinaryModel model, FileName oldName, FileName newName)
{
}
public void NotifyStale(OpenedFile file, IBinaryModel model)
{
}
public void NotifyUnloaded(OpenedFile file, IBinaryModel model)
{
}
}
}

29
src/Main/Base/Project/Workbench/File/DocumentFileModelInfo.cs

@ -0,0 +1,29 @@ @@ -0,0 +1,29 @@
// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt)
// This code is distributed under the GNU LGPL (for details please see \doc\license.txt)
using System;
using System.Text;
namespace ICSharpCode.SharpDevelop.Workbench
{
/// <summary>
/// Extra information about a TextDocument that was loaded from an OpenedFile.
/// </summary>
public class DocumentFileModelInfo
{
public DocumentFileModelInfo()
{
this.Encoding = SD.FileService.DefaultFileEncoding;
}
/// <summary>
/// Gets whether the model is stale.
/// </summary>
public bool IsStale { get; internal set; }
/// <summary>
/// Gets the encoding of the model.
/// </summary>
public Encoding Encoding { get; set; }
}
}

38
src/Main/Base/Project/Workbench/File/FileModels.cs

@ -0,0 +1,38 @@ @@ -0,0 +1,38 @@
// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt)
// This code is distributed under the GNU LGPL (for details please see \doc\license.txt)
using System;
using System.IO;
using ICSharpCode.AvalonEdit.Document;
namespace ICSharpCode.SharpDevelop.Workbench
{
/// <summary>
/// Pre-defined file models.
/// </summary>
public static class FileModels
{
/// <summary>
/// The binary file model provider.
/// </summary>
public static readonly IFileModelProvider<IBinaryModel> Binary = new BinaryFileModelProvider();
/// <summary>
/// The text document file model provider.
/// </summary>
public static readonly IFileModelProvider<TextDocument> TextDocument = new TextDocumentFileModelProvider();
}
/// <summary>
/// Represents the binary contents of a file.
/// </summary>
public interface IBinaryModel
{
/// <summary>
/// Creates a stream that reads from the binary model.
/// </summary>
/// <returns>A readable and seekable stream.</returns>
/// <exception cref="IOException">Error opening the file.</exception>
Stream OpenRead();
}
}

76
src/Main/Base/Project/Workbench/File/IFileModelProvider.cs

@ -0,0 +1,76 @@ @@ -0,0 +1,76 @@
// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt)
// This code is distributed under the GNU LGPL (for details please see \doc\license.txt)
using System;
using ICSharpCode.Core;
namespace ICSharpCode.SharpDevelop.Workbench
{
/// <summary>
/// Interface that deals with loading and saving OpenedFile model instances.
/// Pre-defined model providers are available in the <see cref="FileModels"/> class.
/// Custom IFileModelProvider implementations may be used to add support for additional types of models.
/// </summary>
public interface IFileModelProvider<T> where T : class
{
/// <summary>
/// Loads a file model from the OpenedFile.
/// Most implementations of this method will request a lower-level model (e.g. FileModels.Binary) from the opened file
/// and construct a higher-level representation.
/// </summary>
/// <exception cref="System.IO.IOException">Error loading the file.</exception>
/// <exception cref="FormatException">Cannot construct the model because the underyling data is in an invalid format.</exception>
T Load(OpenedFile file);
/// <summary>
/// Saves a file model.
/// The file should be saved to a location where a subsequent Load() operation can load from.
/// Usually, this means saving to a lower-level model (e.g. a TextDocument model is saved by creating a binary model).
///
/// After the Save() method completes successfully, the specified model may no longer be marked as dirty. For models saving to
/// lower-level models, this is done by marking that lower-level model as dirty.
/// In other cases, the provider may have to explicitly remove the dirty flag from the model.
/// </summary>
void Save(OpenedFile file, T model);
/// <summary>
/// Saves a file model to disk. This method may by-pass lower-level models (such as FileModels.Binary) and directly write to disk.
/// Note that this method is supposed to save a copy of the file, without resetting the dirty flag (think "Save Copy As...").
/// However, transferring the dirty flag to a lower-level model is allowed.
/// </summary>
/// <exception cref="System.IO.IOException">Error saving the file.</exception>
void SaveCopyAs(OpenedFile file, T model, FileName outputFileName);
/// <summary>
/// Gets whether this provider can load from the specified other provider.
/// It is important that CanLoadFrom() is transitive!
/// For a file model provider that loads from a text document model, the implementation should look like this:
/// <code>
/// return otherProvider == FileModels.TextDocument || FileModels.TextDocument.CanLoadFrom(otherProvider);
/// </code>
/// </summary>
bool CanLoadFrom<U>(IFileModelProvider<U> otherProvider) where U : class;
/// <summary>
/// Notifies the provider that a file was renamed.
/// If the file name is already available as part of the model, the provider is responsible for updating the model.
/// </summary>
void NotifyRename(OpenedFile file, T model, FileName oldName, FileName newName);
/// <summary>
/// Notifies the provider that a model was marked as stale.
/// </summary>
void NotifyStale(OpenedFile file, T model);
/// <summary>
/// Notifies a model that it became unloaded.
/// This can happen due to:
/// <list type="items"></list>
/// <item>an explicit <see cref="OpenedFile.UnloadModel"/> call</item>
/// <item>a <see cref="OpenedFile.ReplaceModel"/> call replacing this model with another</item>
/// <item>a <see cref="OpenedFile.GetModel"/> call replacing a stale model with another</item>
/// <item>the OpenedFile getting disposed</item>
/// </summary>
void NotifyUnloaded(OpenedFile file, T model);
}
}

181
src/Main/Base/Project/Workbench/File/OpenedFile.cs

@ -27,21 +27,49 @@ using ICSharpCode.SharpDevelop.Parser; @@ -27,21 +27,49 @@ using ICSharpCode.SharpDevelop.Parser;
namespace ICSharpCode.SharpDevelop.Workbench
{
/// <summary>
/// Represents an opened file.
/// Options for use with <see cref="OpenedFile.GetModel"/>
/// </summary>
public abstract class OpenedFile : ICanBeDirty
[Flags]
public enum GetModelOptions
{
protected IViewContent currentView;
bool inLoadOperation;
bool inSaveOperation;
None = 0,
/// <summary>
/// holds unsaved file content in memory when view containing the file was closed but no other view
/// activated
/// Return stale models without reloading.
/// </summary>
byte[] fileData;
#region IsDirty
AllowStale = 1,
/// <summary>
/// Do not load any models:
/// Returns null if the model is not already loaded, or if it is stale and the AllowStale option isn't in use.
/// </summary>
DoNotLoad = 2,
}
/// <summary>
/// Option that control how <see cref="OpenedFile.ReplaceModel"/> handles the dirty flag.
/// </summary>
public enum ReplaceModelMode
{
/// <summary>
/// The new model is marked as dirty; and any other models are marked as stale.
/// </summary>
SetAsDirty,
/// <summary>
/// The new model is marked as valid; the status of any other models is unchanged.
/// </summary>
SetAsValid,
/// <summary>
/// The new model is marked as dirty, the previously dirty model is marked as stale, and any other models are unchanged.
/// This mode is intended for use in <see cref="IFileModelProvider{T}.Save"/> implementations.
/// </summary>
TransferDirty
}
/// <summary>
/// Represents an opened file.
/// </summary>
public abstract class OpenedFile : ICanBeDirty
{
#region IsDirty implementation
bool isDirty;
public event EventHandler IsDirtyChanged;
@ -50,7 +78,7 @@ namespace ICSharpCode.SharpDevelop.Workbench @@ -50,7 +78,7 @@ namespace ICSharpCode.SharpDevelop.Workbench
/// </summary>
public bool IsDirty {
get { return isDirty;}
set {
private set {
if (isDirty != value) {
isDirty = value;
@ -60,26 +88,16 @@ namespace ICSharpCode.SharpDevelop.Workbench @@ -60,26 +88,16 @@ namespace ICSharpCode.SharpDevelop.Workbench
}
}
}
/// <summary>
/// Marks the file as dirty if it currently is not in a load operation.
/// </summary>
public virtual void MakeDirty()
{
if (!inLoadOperation) {
this.IsDirty = true;
}
}
#endregion
bool isUntitled;
#region FileName
/// <summary>
/// Gets if the file is untitled. Untitled files show a "Save as" dialog when they are saved.
/// </summary>
public bool IsUntitled {
get { return isUntitled; }
protected set { isUntitled = value; }
get {
return fileName.ToString().StartsWith("untitled:", StringComparison.Ordinal);
}
}
FileName fileName;
@ -96,6 +114,11 @@ namespace ICSharpCode.SharpDevelop.Workbench @@ -96,6 +114,11 @@ namespace ICSharpCode.SharpDevelop.Workbench
}
}
/// <summary>
/// Occurs when the file name has changed.
/// </summary>
public event EventHandler FileNameChanged;
protected virtual void ChangeFileName(FileName newValue)
{
SD.MainThread.VerifyAccess();
@ -106,13 +129,112 @@ namespace ICSharpCode.SharpDevelop.Workbench @@ -106,13 +129,112 @@ namespace ICSharpCode.SharpDevelop.Workbench
FileNameChanged(this, EventArgs.Empty);
}
}
#endregion
/// <summary>
/// Occurs when the file name has changed.
/// This method sets all models to 'stale', causing the file to be re-loaded from disk
/// on the next GetModel() call.
/// </summary>
public event EventHandler FileNameChanged;
/// <exception cref="InvalidOperationException">The file is untitled.</exception>
public void ReloadFromDisk()
{
if (IsUntitled)
throw new InvalidOperationException("Cannot reload an untitled file from disk.");
throw new NotImplementedException();
}
/// <summary>
/// Saves the file to disk.
/// </summary>
/// <remarks>If the file is saved successfully, the dirty flag will be cleared (the dirty model becomes valid instead).</remarks>
/// <exception cref="InvalidOperationException">The file is untitled.</exception>
public void SaveToDisk()
{
if (IsUntitled)
throw new InvalidOperationException("Cannot reload an untitled file from disk.");
throw new NotImplementedException();
}
/// <summary>
/// Changes the file name, and saves the file to disk.
/// </summary>
/// <remarks>If the file is saved successfully, the dirty flag will be cleared (the dirty model becomes valid instead).</remarks>
public void SaveToDisk(FileName fileName)
{
this.FileName = fileName;
SaveToDisk();
}
/// <summary>
/// Saves a copy of the file to disk. Does not change the name of the OpenedFile to the specified file name, and does not reset the dirty flag.
/// </summary>
public void SaveCopyAs(FileName fileName)
{
throw new NotImplementedException();
}
/// <summary>
/// Retrieves a file model, loading it if necessary.
/// </summary>
/// <param name="modelProvider">The model provider for the desired model type. Built-in model providers can be found in the <see cref="FileModels"/> class.</param>
/// <param name="options">Options that control how</param>
/// <returns>The model instance, or possibly <c>null</c> if <c>GetModelOptions.DoNotLoad</c> is in use.</returns>
/// <exception cref="IOException">Error loading the file.</exception>
/// <exception cref="FormatException">Cannot construct the model because the underyling data is in an invalid format.</exception>
public T GetModel<T>(IFileModelProvider<T> modelProvider, GetModelOptions options = GetModelOptions.None) where T : class
{
throw new NotImplementedException();
}
/// <summary>
/// Sets the model associated with the specified model provider to be dirty.
/// All other models are marked as stale. If another model was previously dirty, those earlier changes will be lost.
/// </summary>
public void MakeDirty<T>(IFileModelProvider<T> modelProvider) where T : class
{
throw new NotImplementedException();
}
/// <summary>
/// Unloads the model associated with the specified model provider.
/// Unloading the dirty model will cause changes to be lost.
/// </summary>
public void UnloadModel<T>(IFileModelProvider<T> modelProvider) where T : class
{
throw new NotImplementedException();
}
/// <summary>
/// Replaces the model associated with the specified model provider with a different instance.
/// </summary>
/// <param name="modelProvider">The model provider for the model type.</param>
/// <param name="model">The new model instance.</param>
/// <param name="mode">Specifies how the dirty flag is handled during the replacement.
/// By default, the new model is marked as dirty and all other models are marked as stale.
/// In <see cref="IFileModelProvider{T}.Save"/> implementations, you should use <see cref="ReplaceModelMode.TransferDirty"/> instead.</param>
public void ReplaceModel<T>(IFileModelProvider<T> modelProvider, T model, ReplaceModelMode mode = ReplaceModelMode.SetAsDirty) where T : class
{
throw new NotImplementedException();
}
}
/*
/// <summary>
/// Represents an opened file.
/// </summary>
public abstract class OpenedFile : ICanBeDirty
{
protected IViewContent currentView;
bool inLoadOperation;
bool inSaveOperation;
/// <summary>
/// holds unsaved file content in memory when view containing the file was closed but no other view
/// activated
/// </summary>
byte[] fileData;
public abstract event EventHandler FileClosed;
/// <summary>
/// Use this method to save the file to disk using a new name.
@ -378,4 +500,5 @@ namespace ICSharpCode.SharpDevelop.Workbench @@ -378,4 +500,5 @@ namespace ICSharpCode.SharpDevelop.Workbench
}
}
}
*/
}

89
src/Main/Base/Project/Workbench/File/TextDocumentFileModelProvider.cs

@ -0,0 +1,89 @@ @@ -0,0 +1,89 @@
// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt)
// This code is distributed under the GNU LGPL (for details please see \doc\license.txt)
using System;
using System.Collections.Generic;
using System.ComponentModel.Design;
using System.Diagnostics;
using System.IO;
using System.Text;
using ICSharpCode.AvalonEdit.Document;
using ICSharpCode.AvalonEdit.Utils;
using ICSharpCode.Core;
using ICSharpCode.SharpDevelop.Widgets.MyersDiff;
namespace ICSharpCode.SharpDevelop.Workbench
{
sealed class TextDocumentFileModelProvider : IFileModelProvider<TextDocument>
{
public TextDocument Load(OpenedFile file)
{
TextDocument document = file.GetModel(this, GetModelOptions.AllowStale | GetModelOptions.DoNotLoad);
var info = document != null ? document.GetFileModelInfo() : new DocumentFileModelInfo();
string textContent;
using (Stream stream = file.GetModel(FileModels.Binary).OpenRead()) {
using (StreamReader reader = FileReader.OpenStream(stream, SD.FileService.DefaultFileEncoding)) {
textContent = reader.ReadToEnd();
info.Encoding = reader.CurrentEncoding;
}
}
if (document != null) {
// Reload document
var diff = new MyersDiffAlgorithm(new StringSequence(document.Text), new StringSequence(textContent));
document.Replace(0, document.TextLength, textContent, ToOffsetChangeMap(diff.GetEdits()));
document.UndoStack.ClearAll();
info.IsStale = false;
return document;
} else {
document = new TextDocument(textContent);
document.GetRequiredService<IServiceContainer>().AddService(typeof(DocumentFileModelInfo), info);
}
}
OffsetChangeMap ToOffsetChangeMap(IEnumerable<Edit> edits)
{
var map = new OffsetChangeMap();
int diff = 0;
foreach (var edit in edits) {
Debug.Assert(edit.EditType != ChangeType.None && edit.EditType != ChangeType.Unsaved);
int offset = edit.BeginA + diff;
int removalLength = edit.EndA - edit.BeginA;
int insertionLength = edit.EndB - edit.BeginB;
diff += (insertionLength - removalLength);
map.Add(new OffsetChangeMapEntry(offset, removalLength, insertionLength));
}
return map;
}
public void Save(OpenedFile file, TextDocument model)
{
throw new NotImplementedException();
}
public void SaveCopyAs(OpenedFile file, TextDocument model, FileName outputFileName)
{
throw new NotImplementedException();
}
bool IFileModelProvider<TextDocument>.CanLoadFrom<U>(IFileModelProvider<U> otherProvider)
{
return otherProvider == FileModels.Binary || FileModels.Binary.CanLoadFrom(otherProvider);
}
public void NotifyRename(OpenedFile file, TextDocument model, FileName oldName, FileName newName)
{
model.FileName = newName;
}
public void NotifyStale(OpenedFile file, TextDocument model)
{
model.GetFileModelInfo().IsStale = true;
}
public void NotifyUnloaded(OpenedFile file, TextDocument model)
{
model.GetFileModelInfo().IsStale = true;
}
}
}

9
src/Main/Core/Project/Src/Services/FileUtility/PathName.cs

@ -57,6 +57,15 @@ namespace ICSharpCode.Core @@ -57,6 +57,15 @@ namespace ICSharpCode.Core
return normalizedPath;
}
/// <summary>
/// Converts the path into an URI.
/// </summary>
/// <exception cref="UriFormatException">The path contains invalid characters for an URI.</exception>
public Uri ToUri()
{
return new Uri(normalizedPath, IsRelative ? UriKind.Relative : UriKind.Absolute);
}
/// <summary>
/// Gets whether this path is relative.
/// </summary>

Loading…
Cancel
Save