diff --git a/src/AEGIS.sln b/src/AEGIS.sln
index c36ca2b..dab56eb 100644
--- a/src/AEGIS.sln
+++ b/src/AEGIS.sln
@@ -1,7 +1,7 @@
Microsoft Visual Studio Solution File, Format Version 12.00
-# Visual Studio 15
-VisualStudioVersion = 15.0.27130.2027
+# Visual Studio Version 16
+VisualStudioVersion = 16.0.30907.101
MinimumVisualStudioVersion = 10.0.40219.1
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{0FA79630-580C-4AFF-A3E5-04DFE27ED91C}"
ProjectSection(SolutionItems) = preProject
@@ -31,6 +31,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Tests.Numerics", "Tests.Num
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Tests.Storage.Hadoop", "Tests.Storage.Hadoop\Tests.Storage.Hadoop.csproj", "{23452575-F4C0-45F6-A857-23F5320DBBDF}"
EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "IO", "IO\IO.csproj", "{275B88F0-DA62-49C3-B586-47F5A6C22D2F}"
+EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@@ -81,6 +83,10 @@ Global
{23452575-F4C0-45F6-A857-23F5320DBBDF}.Debug|Any CPU.Build.0 = Debug|Any CPU
{23452575-F4C0-45F6-A857-23F5320DBBDF}.Release|Any CPU.ActiveCfg = Release|Any CPU
{23452575-F4C0-45F6-A857-23F5320DBBDF}.Release|Any CPU.Build.0 = Release|Any CPU
+ {275B88F0-DA62-49C3-B586-47F5A6C22D2F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {275B88F0-DA62-49C3-B586-47F5A6C22D2F}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {275B88F0-DA62-49C3-B586-47F5A6C22D2F}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {275B88F0-DA62-49C3-B586-47F5A6C22D2F}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
diff --git a/src/AEGIS.sln.DotSettings b/src/AEGIS.sln.DotSettings
new file mode 100644
index 0000000..8ec2a39
--- /dev/null
+++ b/src/AEGIS.sln.DotSettings
@@ -0,0 +1,14 @@
+
+ <copyright file="$FILENAME$" company="Eötvös Loránd University (ELTE)">
+ Copyright 2016-$CURRENT_YEAR$ Roberto Giachetta. Licensed under the
+ Educational Community License, Version 2.0 (the "License"); you may
+ not use this file except in compliance with the License. You may
+ obtain a copy of the License at
+ http://opensource.org/licenses/ECL-2.0
+
+ Unless required by applicable law or agreed to in writing,
+ software distributed under the License is distributed on an "AS IS"
+ BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
+ or implied. See the License for the specific language governing
+ permissions and limitations under the License.
+</copyright>
\ No newline at end of file
diff --git a/src/Core/Conditions.cs b/src/Core/Conditions.cs
new file mode 100644
index 0000000..562756c
--- /dev/null
+++ b/src/Core/Conditions.cs
@@ -0,0 +1,198 @@
+//
+// Copyright (c) 2011-2019 Roberto Giachetta. Licensed under the
+// Educational Community License, Version 2.0 (the "License"); you may
+// not use this file except in compliance with the License. You may
+// obtain a copy of the License at
+// http://opensource.org/licenses/ECL-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an "AS IS"
+// BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
+// or implied. See the License for the specific language governing
+// permissions and limitations under the License.
+//
+
+using System;
+using System.Collections.Generic;
+using System.Linq;
+
+namespace AEGIS
+{
+ ///
+ /// Defines a collection of conditions that can be applied to any value.
+ ///
+ ///
+ /// This type provides easy to use methods for declaring requirements for any value.
+ /// The predicates return true if the value satisfies the requirements; otherwise, false .
+ /// The predicates never throw exception.
+ ///
+ public static class Conditions
+ {
+ ///
+ /// The value has no requirements.
+ ///
+ /// A that is always true.
+ public static Predicate None()
+ {
+ return (value => true);
+ }
+
+ ///
+ /// Requires that the value is positive.
+ ///
+ /// A that validates whether the value is positive.
+ public static Predicate IsPositive()
+ {
+ return (value => (value is IConvertible) && Convert.ToDouble(value) > 0);
+ }
+
+ ///
+ /// Requires that the value is negative.
+ ///
+ /// A that validates whether the value is negative.
+ public static Predicate IsNegative()
+ {
+ return (value => (value is IConvertible) && Convert.ToDouble(value) < 0);
+ }
+
+ ///
+ /// Requires that the value is not positive.
+ ///
+ /// A that validates whether the value is not positive.
+ public static Predicate IsNotPositive()
+ {
+ return (value => (value is IConvertible) && Convert.ToDouble(value) <= 0);
+ }
+
+ ///
+ /// Requires that the value is not negative.
+ ///
+ /// A that validates whether the value is not negative.
+ public static Predicate IsNotNegative()
+ {
+ return (value => (value is IConvertible) && Convert.ToDouble(value) >= 0);
+ }
+
+ ///
+ /// Requires that the value is even.
+ ///
+ /// A that validates whether the value is even.
+ public static Predicate IsEven()
+ {
+ return (value => (value is IConvertible) && Convert.ToInt32(value) % 2 == 1);
+ }
+
+ ///
+ /// Requires that the value is odd.
+ ///
+ /// A that validates whether the value is odd.
+ public static Predicate IsOdd()
+ {
+ return (value => (value is IConvertible) && Convert.ToInt32(value) % 2 == 1);
+ }
+
+ ///
+ /// Requires that the value is greater than or equal to the specified boundary.
+ ///
+ /// The boundary.
+ /// A that validates whether the value is greater than or equal to the specified boundary.
+ public static Predicate IsGreaterThanOrEqualTo(Double boundary)
+ {
+ return (value => (value is IConvertible) && boundary <= Convert.ToDouble(value));
+ }
+
+ ///
+ /// Requires that the value is greater than the specified boundary.
+ ///
+ /// The boundary.
+ /// A that validates whether the value is greater than the specified boundary.
+ public static Predicate IsGreaterThan(Double boundary)
+ {
+ return (value => (value is IConvertible) && boundary < Convert.ToDouble(value));
+ }
+
+ ///
+ /// Requires that the value is less than or equal to the specified boundary.
+ ///
+ /// The boundary.
+ /// A that validates whether the value is less than or equal to the specified boundary.
+ public static Predicate IsLessThanOrEqualTo(Double boundary)
+ {
+ return (value => (value is IConvertible) && boundary >= Convert.ToDouble(value));
+ }
+
+ ///
+ /// Requires that the value is less than the specified boundary.
+ ///
+ /// The boundary.
+ /// A that validates whether the value is less than the specified boundary.
+ public static Predicate IsLessThan(Double boundary)
+ {
+ return (value => (value is IConvertible) && boundary > Convert.ToDouble(value));
+ }
+
+ ///
+ /// Requires that the value is between the specified lower and upper boundaries.
+ ///
+ /// The lower boundary.
+ /// The upper boundary.
+ /// A that validates whether the value is between the specified lower and upper boundaries.
+ public static Predicate IsBetween(Double lowerBoundary, Double upperBoundary)
+ {
+ return (value => (value is IConvertible) && lowerBoundary <= Convert.ToDouble(value) && Convert.ToDouble(value) <= upperBoundary);
+ }
+
+ ///
+ /// Requires that the value is strictly between the specified lower and upper boundaries.
+ ///
+ /// The lower boundary.
+ /// The upper boundary.
+ /// A that validates whether the value is strictly between the specified lower and upper boundaries.
+ public static Predicate IsStricklyBetween(Double lowerBoundary, Double upperBoundary)
+ {
+ return (value => (value is IConvertible) && lowerBoundary < Convert.ToDouble(value) && Convert.ToDouble(value) < upperBoundary);
+ }
+
+ ///
+ /// Requires that the is one of the specified objects.
+ ///
+ /// The objects.
+ /// A that validates whether the value is one of the specified objects.
+ public static Predicate IsOneOf(params Object[] objects)
+ {
+ return (value => objects.Contains(value));
+ }
+
+ ///
+ /// Requires that the is one of the specified objects.
+ ///
+ /// The objects.
+ /// A that validates whether the value is one of the specified objects.
+ public static Predicate IsOneOf(IEnumerable objects)
+ {
+ return (value => objects.Contains(value));
+ }
+
+ ///
+ /// Requires that the object (or type) implements the specified interface.
+ ///
+ /// The interface type.
+ /// A that validates that the object (or type) implements the specified interface.
+ public static Predicate Implements()
+ {
+ return value => (value is Type) && ((value as Type).Equals(typeof(InterfaceType)) || (value as Type).GetInterfaces().Contains(typeof(InterfaceType)))
+ || (value is InterfaceType);
+ }
+
+ ///
+ /// Requires that the object (or type) inherits the specified superclass type.
+ ///
+ /// The superclass type.
+ /// A that validates that the object (or type) inherits the specified superclass type.
+ public static Predicate Inherits()
+ {
+ return value => (value is Type) && ((value as Type).Equals(typeof(ClassType)) || (value as Type).IsSubclassOf(typeof(ClassType)) || (value as Type).GetInterfaces().Contains(typeof(ClassType)))
+ || (value is ClassType);
+ }
+ }
+}
diff --git a/src/Core/GeometryModel.cs b/src/Core/GeometryModel.cs
new file mode 100644
index 0000000..0839085
--- /dev/null
+++ b/src/Core/GeometryModel.cs
@@ -0,0 +1,70 @@
+//
+// Copyright 2016-2021 Roberto Giachetta. Licensed under the
+// Educational Community License, Version 2.0 (the "License"); you may
+// not use this file except in compliance with the License. You may
+// obtain a copy of the License at
+// http://opensource.org/licenses/ECL-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an "AS IS"
+// BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
+// or implied. See the License for the specific language governing
+// permissions and limitations under the License.
+//
+
+using System;
+
+namespace AEGIS
+{
+ ///
+ /// Defines the supported models for geometry representation.
+ ///
+ [Flags]
+ public enum GeometryModel
+ {
+ ///
+ /// No spatial or temporal support is specified.
+ ///
+ None = 1,
+
+ ///
+ /// Spatial with 2 dimensional coordinate system.
+ ///
+ Spatial2D = 2,
+
+ ///
+ /// Spatial with 3 dimensional coordinate system.
+ ///
+ Spatial3D = 4,
+
+ ///
+ /// Spatial with 2 or 3 dimensional coordinate system.
+ ///
+ Spatial = 6,
+
+ ///
+ /// Spatio-temporal with 2 dimensional spatial coordinate system and one dimensional temporal coordinate system.
+ ///
+ SpatioTemporal2D = 8,
+
+ ///
+ /// Spatio-temporal with 3 dimensional spatial coordinate system and one dimensional temporal coordinate system.
+ ///
+ SpatioTemporal3D = 16,
+
+ ///
+ /// Spatio-temporal with 2 or 3 dimensional spatial coordinate system and one dimensional temporal coordinate system.
+ ///
+ SpatioTemporal = 24,
+
+ ///
+ /// Spatial or spatio-temporal with 2 or 3 dimensional spatial coordinate system and optionally one dimensional temporal coordinate system.
+ ///
+ SpatialOrSpatioTemporal = 30,
+
+ ///
+ /// Support for all models.
+ ///
+ Any = 31
+ }
+}
diff --git a/src/Core/IPolygon.cs b/src/Core/IPolygon.cs
index 10818db..fee64d4 100644
--- a/src/Core/IPolygon.cs
+++ b/src/Core/IPolygon.cs
@@ -49,5 +49,13 @@ public interface IPolygon : ISurface, IBasicPolygon
/// The index is equal to or greater than the number of coordinates.
///
new ILinearRing GetHole(Int32 index);
+
+ ///
+ /// Add a hole to the polygon.
+ ///
+ /// The hole.
+ /// The hole is null.
+ /// The reference system of the hole does not match the reference system of the polygon.
+ void AddHole(ILinearRing hole);
}
}
diff --git a/src/Core/IdentifiedObjectCollectionAttribute.cs b/src/Core/IdentifiedObjectCollectionAttribute.cs
new file mode 100644
index 0000000..a253a36
--- /dev/null
+++ b/src/Core/IdentifiedObjectCollectionAttribute.cs
@@ -0,0 +1,59 @@
+//
+// Copyright (c) 2011-2019 Roberto Giachetta. Licensed under the
+// Educational Community License, Version 2.0 (the "License"); you may
+// not use this file except in compliance with the License. You may
+// obtain a copy of the License at
+// http://opensource.org/licenses/ECL-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an "AS IS"
+// BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
+// or implied. See the License for the specific language governing
+// permissions and limitations under the License.
+//
+
+namespace AEGIS
+{
+ using System;
+
+ ///
+ /// Indicates that the type is a collection of known identified object instances.
+ ///
+ ///
+ /// The supported type is a static class with properties representing the instances of a specified subclass.
+ /// The class must also support a query property for all known instances and methods for identifier and name queries.
+ ///
+ [AttributeUsage(AttributeTargets.Class, Inherited = false, AllowMultiple = true)]
+ public class IdentifiedObjectCollectionAttribute : Attribute
+ {
+ #region Public properties
+
+ ///
+ /// Gets the type of the instances.
+ ///
+ /// The base type for all instances in the collection.
+ public Type Type { get; private set; }
+
+ #endregion
+
+ #region Constructors
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// The base type for all instances in the collection.
+ /// The types is null.
+ /// The type is not a subclass of .
+ public IdentifiedObjectCollectionAttribute(Type type)
+ {
+ if (type == null)
+ throw new ArgumentNullException("type", "The types is null.");
+ if (!type.IsSubclassOf(typeof(IdentifiedObject)))
+ throw new ArgumentException("The type is not a subclass of IdentifiedObject.", "type");
+
+ Type = type;
+ }
+
+ #endregion
+ }
+}
diff --git a/src/IO/BufferingMode.cs b/src/IO/BufferingMode.cs
new file mode 100644
index 0000000..a8aa5ca
--- /dev/null
+++ b/src/IO/BufferingMode.cs
@@ -0,0 +1,42 @@
+//
+// Copyright 2016-2021 Roberto Giachetta. Licensed under the
+// Educational Community License, Version 2.0 (the "License"); you may
+// not use this file except in compliance with the License. You may
+// obtain a copy of the License at
+// http://opensource.org/licenses/ECL-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an "AS IS"
+// BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
+// or implied. See the License for the specific language governing
+// permissions and limitations under the License.
+//
+
+namespace AEGIS.IO
+{
+ ///
+ /// Defines the possible buffering modes.
+ ///
+ public enum BufferingMode
+ {
+ ///
+ /// Indicates that the default buffering mode is used.
+ ///
+ Default,
+
+ ///
+ /// Indicates no buffering.
+ ///
+ None,
+
+ ///
+ /// Indicates minimal buffering.
+ ///
+ Minimal,
+
+ ///
+ /// Indicates maximal buffering.
+ ///
+ Maximal
+ }
+}
diff --git a/src/IO/GeometryStreamFormat.cs b/src/IO/GeometryStreamFormat.cs
new file mode 100644
index 0000000..2593dad
--- /dev/null
+++ b/src/IO/GeometryStreamFormat.cs
@@ -0,0 +1,127 @@
+//
+// Copyright 2016-2021 Roberto Giachetta. Licensed under the
+// Educational Community License, Version 2.0 (the "License"); you may
+// not use this file except in compliance with the License. You may
+// obtain a copy of the License at
+// http://opensource.org/licenses/ECL-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an "AS IS"
+// BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
+// or implied. See the License for the specific language governing
+// permissions and limitations under the License.
+//
+
+using System;
+using System.Linq;
+
+namespace AEGIS.IO
+{
+ ///
+ /// Represents a format of geometry stream.
+ ///
+ public class GeometryStreamFormat : IdentifiedObject
+ {
+ #region Public properties
+
+ ///
+ /// Gets or sets the version of the format.
+ ///
+ /// The version of the format.
+ public String Version { get; private set; }
+
+ ///
+ /// Gets the file extensions of the format.
+ ///
+ /// The array containing the file extensions of the format.
+ public String[] Extensions { get; private set; }
+
+ ///
+ /// Gets the internet media types of the format.
+ ///
+ /// The array containing the internet media types of the format.
+ public String[] InternetMediaTypes { get; private set; }
+
+ ///
+ /// Gets the supported geometry types of the format.
+ ///
+ /// The array containing the supported geometry types of the format.
+ public Type[] SupportedGeometries { get; private set; }
+
+ ///
+ /// Gets or sets the geometry models supported by the format.
+ ///
+ /// The array containing the geometry models supported by the format.
+ public GeometryModel[] SupportedModels { get; private set; }
+
+ ///
+ /// Gets the parameters of the format.
+ ///
+ /// The array containing the parameters of the format.
+ public GeometryStreamParameter[] Parameters { get; private set; }
+
+ #endregion
+
+ #region Constructors
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// The identifier.
+ /// The name.
+ /// The version.
+ /// The extensions of the input/output data source.
+ /// The internet media types.
+ /// The supported geometries of the format.
+ /// The supported models of the format.
+ /// The parameters of the format.
+ public GeometryStreamFormat(String identifier, String name, String version,
+ String[] extensions, String[] internetMediaTypes,
+ Type[] supportedGeometries, GeometryModel[] supportedModels,
+ params GeometryStreamParameter[] parameters)
+ : this(identifier, name, null, null, version, extensions, internetMediaTypes, supportedGeometries, supportedModels, parameters)
+ {
+ }
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// The identifier.
+ /// The name.
+ /// The remarks.
+ /// The aliases.
+ /// The version.
+ /// The extensions of the input/output data source.
+ /// The internet media types.
+ /// The supported geometries of the format.
+ /// The supported models of the format.
+ /// The parameters of the format.
+ public GeometryStreamFormat(String identifier, String name, String remarks, String[] aliases, String version,
+ String[] extensions, String[] internetMediaTypes,
+ Type[] supportedGeometries, GeometryModel[] supportedModels,
+ params GeometryStreamParameter[] parameters)
+ : base(identifier, name, remarks, aliases)
+ {
+ Version = version;
+ Extensions = extensions;
+ InternetMediaTypes = internetMediaTypes;
+ SupportedGeometries = supportedGeometries;
+ SupportedModels = supportedModels;
+ Parameters = parameters;
+ }
+
+ #endregion
+
+ ///
+ /// Determines whether the specified geometry is supported by the format.
+ ///
+ /// The geometry.
+ /// true if the specified geometry is supported by the format; otherwise, false .
+ public Boolean Supports(IGeometry geometry)
+ {
+ if (geometry == null)
+ throw new ArgumentNullException("geometry", "The geometry is null.");
+
+ return SupportedGeometries.Any(type => type.IsInstanceOfType(geometry));
+ }
+ }
+}
diff --git a/src/IO/GeometryStreamFormats.cs b/src/IO/GeometryStreamFormats.cs
new file mode 100644
index 0000000..34d7c8c
--- /dev/null
+++ b/src/IO/GeometryStreamFormats.cs
@@ -0,0 +1,107 @@
+//
+// Copyright 2016-2021 Roberto Giachetta. Licensed under the
+// Educational Community License, Version 2.0 (the "License"); you may
+// not use this file except in compliance with the License. You may
+// obtain a copy of the License at
+// http://opensource.org/licenses/ECL-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an "AS IS"
+// BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
+// or implied. See the License for the specific language governing
+// permissions and limitations under the License.
+//
+
+using System;
+using System.Collections.Generic;
+using System.Linq;
+
+namespace AEGIS.IO
+{
+ [IdentifiedObjectCollection(typeof(GeometryStreamFormat))]
+ public static class GeometryStreamFormats
+ {
+ #region Query fields
+
+ private static GeometryStreamFormat[] _all;
+
+ #endregion
+
+ #region Query properties
+
+ ///
+ /// Gets all instances within the collection.
+ ///
+ /// A read-only list containing all instances within the collection.
+ public static IList All
+ {
+ get
+ {
+ if (_all == null)
+ _all = typeof(GeometryStreamFormats).GetProperties().
+ Where(property => property.Name != "All").
+ Select(property => property.GetValue(null, null) as GeometryStreamFormat).
+ ToArray();
+ return Array.AsReadOnly(_all);
+ }
+ }
+
+ #endregion
+
+ #region Query methods
+
+ ///
+ /// Returns all instances matching a specified identifier.
+ ///
+ /// The identifier.
+ /// A list containing the instances that match the specified identifier.
+ public static IList FromIdentifier(String identifier)
+ {
+ if (identifier == null)
+ return null;
+
+ return All.Where(obj => System.Text.RegularExpressions.Regex.IsMatch(obj.Identifier, identifier)).ToList().AsReadOnly();
+ }
+ ///
+ /// Returns all instances matching a specified name.
+ ///
+ /// The name.
+ /// A list containing the instances that match the specified name.
+ public static IList FromName(String name)
+ {
+ if (name == null)
+ return null;
+
+ return All.Where(obj => System.Text.RegularExpressions.Regex.IsMatch(obj.Name, name) ||
+ obj.Aliases != null && obj.Aliases.Any(alias => System.Text.RegularExpressions.Regex.IsMatch(alias, name, System.Text.RegularExpressions.RegexOptions.IgnoreCase))).ToList().AsReadOnly();
+ }
+
+ #endregion
+
+ #region Private static fields
+
+ private static GeometryStreamFormat _shapefile;
+
+ #endregion
+
+ #region Public static fields
+
+ ///
+ /// Esri shapefile.
+ ///
+ public static GeometryStreamFormat Shapefile
+ {
+ get
+ {
+ return _shapefile ?? (_shapefile =
+ new GeometryStreamFormat("AEGIS::610101", "Esri shapefile",
+ "© 1997, 1998 Environmental Systems Research Institute (ESRI), Inc.", new String[] { "Shapefile" }, "1.0",
+ new String[] { "shp" }, null,
+ new Type[] { typeof(IPoint), typeof(IGeometryCollection), typeof(ILineString), typeof(IPolygon), typeof(IGeometryCollection), typeof(IMultiPolygon) },
+ new GeometryModel[] { GeometryModel.Spatial2D, GeometryModel.Spatial3D }));
+ }
+ }
+
+ #endregion
+ }
+}
diff --git a/src/IO/GeometryStreamParameter.cs b/src/IO/GeometryStreamParameter.cs
new file mode 100644
index 0000000..3577aed
--- /dev/null
+++ b/src/IO/GeometryStreamParameter.cs
@@ -0,0 +1,739 @@
+//
+// Copyright 2016-2021 Roberto Giachetta. Licensed under the
+// Educational Community License, Version 2.0 (the "License"); you may
+// not use this file except in compliance with the License. You may
+// obtain a copy of the License at
+// http://opensource.org/licenses/ECL-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an "AS IS"
+// BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
+// or implied. See the License for the specific language governing
+// permissions and limitations under the License.
+//
+
+using System;
+using System.Collections.Generic;
+
+namespace AEGIS.IO
+{
+ ///
+ /// Represents a parameter of a geometry stream format.
+ ///
+ public class GeometryStreamParameter : IdentifiedObject
+ {
+ #region Private fields
+
+ private readonly Type _type;
+ private readonly Boolean _isOptional;
+ private readonly Object _defaultValue;
+ private readonly Predicate[] _conditions;
+
+ #endregion
+
+ #region Public properties
+
+ ///
+ /// Gets the type declaration of the parameter.
+ ///
+ /// The type declaration of the parameter.
+ public Type Type { get { return _type; } }
+
+ ///
+ /// Gets a value indication whether the parameter is optional.
+ ///
+ /// true if the parameter is optional; otherwise, false .
+ public Boolean IsOptional { get { return _isOptional; } }
+
+ ///
+ /// Gets the default value of the parameter.
+ ///
+ /// The default value of the parameter.
+ public Object DefaultValue { get { return _defaultValue; } }
+
+ ///
+ /// Gets the conditions the parameter value must satisfy.
+ ///
+ /// The conditions the parameter value must satisfy.
+ public IList> Conditions { get { return Array.AsReadOnly(_conditions); } }
+
+ #endregion
+
+ #region Constructors
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// The identifier.
+ /// The name.
+ /// The remarks.
+ /// The aliases.
+ /// The type declaration of the parameter.
+ /// A value indicating whether the parameter is optional.
+ /// The default value of the parameter (if optional).
+ /// The conditions the parameter value must satisfy.
+ ///
+ /// The identifier is null.
+ /// or
+ /// The type is null.
+ ///
+ public GeometryStreamParameter(String identifier, String name, String remarks, String[] aliases, Type type, Boolean isOptional, Object defaultValue, params Predicate[] conditions)
+ : base(identifier, name, remarks, aliases)
+ {
+ if (type == null)
+ throw new ArgumentNullException("type", "The type is null.");
+
+ _type = type;
+ _isOptional = isOptional;
+ _defaultValue = defaultValue ?? (type.IsValueType ? Activator.CreateInstance(type) : null);
+ _conditions = conditions;
+ }
+
+ #endregion
+
+ #region Public methods
+
+ ///
+ /// Determines whether the specified value is valid for the parameter.
+ ///
+ /// The value.
+ /// true if meets all conditions; otherwise, false .
+ public Boolean IsValid(Object value)
+ {
+ if (_conditions == null || _conditions.Length == 0)
+ return true;
+
+ for (Int32 i = 0; i < _conditions.Length; i++)
+ if (!_conditions[i](value)) return false;
+
+ return true;
+ }
+
+ #endregion
+
+ #region Public static factory methods
+
+ ///
+ /// Creates an optional .
+ ///
+ /// The type of the parameter.
+ /// The identifier.
+ /// The name.
+ /// The remarks.
+ /// The produced operation parameter.
+ ///
+ /// The identifier is null.
+ /// or
+ /// The type is null.
+ ///
+ public static GeometryStreamParameter CreateOptionalParameter(String identifier, String name, String remarks)
+ {
+ return new GeometryStreamParameter(identifier, name, remarks, null, typeof(T), true, null, null);
+ }
+
+ ///
+ /// Creates an optional .
+ ///
+ /// The type of the parameter.
+ /// The identifier.
+ /// The name.
+ /// The remarks.
+ /// The default value of the parameter.
+ /// The produced operation parameter.
+ ///
+ /// The identifier is null.
+ /// or
+ /// The type is null.
+ ///
+ public static GeometryStreamParameter CreateOptionalParameter(String identifier, String name, String remarks, T defaultValue)
+ {
+ return new GeometryStreamParameter(identifier, name, remarks, null, typeof(T), true, defaultValue, null);
+ }
+
+ ///
+ /// Creates an optional .
+ ///
+ /// The type of the parameter.
+ /// The identifier.
+ /// The name.
+ /// The remarks.
+ /// The aliases.
+ /// The produced operation parameter.
+ ///
+ /// The identifier is null.
+ /// or
+ /// The type is null.
+ ///
+ public static GeometryStreamParameter CreateOptionalParameter(String identifier, String name, String remarks, String[] aliases)
+ {
+ return new GeometryStreamParameter(identifier, name, remarks, aliases, typeof(T), true, null, null);
+ }
+
+ ///
+ /// Creates an optional .
+ ///
+ /// The type of the parameter.
+ /// The identifier.
+ /// The name.
+ /// The remarks.
+ /// The aliases.
+ /// The default value of the parameter.
+ /// The produced operation parameter.
+ ///
+ /// The identifier is null.
+ /// or
+ /// The type is null.
+ ///
+ public static GeometryStreamParameter CreateOptionalParameter(String identifier, String name, String remarks, String[] aliases, T defaultValue)
+ {
+ return new GeometryStreamParameter(identifier, name, remarks, aliases, typeof(T), true, defaultValue, null);
+ }
+
+ ///
+ /// Creates an optional .
+ ///
+ /// The type of the parameter.
+ /// The identifier.
+ /// The name.
+ /// The remarks.
+ /// The conditions the parameter value must satisfy.
+ /// The produced operation parameter.
+ ///
+ /// The identifier is null.
+ /// or
+ /// The type is null.
+ ///
+ public static GeometryStreamParameter CreateOptionalParameter(String identifier, String name, String remarks, params Predicate[] conditions)
+ {
+ return new GeometryStreamParameter(identifier, name, remarks, null, typeof(T), true, null, conditions);
+ }
+
+ ///
+ /// Creates an optional .
+ ///
+ /// The type of the parameter.
+ /// The identifier.
+ /// The name.
+ /// The remarks.
+ /// The default value of the parameter.
+ /// The conditions the parameter value must satisfy.
+ /// The produced operation parameter.
+ ///
+ /// The identifier is null.
+ /// or
+ /// The type is null.
+ ///
+ public static GeometryStreamParameter CreateOptionalParameter(String identifier, String name, String remarks, T defaultValue, params Predicate[] conditions)
+ {
+ return new GeometryStreamParameter(identifier, name, remarks, null, typeof(T), true, defaultValue, conditions);
+ }
+
+ ///
+ /// Creates an optional .
+ ///
+ /// The type of the parameter.
+ /// The identifier.
+ /// The name.
+ /// The remarks.
+ /// The aliases.
+ /// The conditions the parameter value must satisfy.
+ /// The produced operation parameter.
+ ///
+ /// The identifier is null.
+ /// or
+ /// The type is null.
+ ///
+ public static GeometryStreamParameter CreateOptionalParameter(String identifier, String name, String remarks, String[] aliases, params Predicate[] conditions)
+ {
+ return new GeometryStreamParameter(identifier, name, remarks, aliases, typeof(T), true, null, conditions);
+ }
+
+ ///
+ /// Creates an optional .
+ ///
+ /// The type of the parameter.
+ /// The identifier.
+ /// The name.
+ /// The remarks.
+ /// The aliases.
+ /// The default value of the parameter.
+ /// The conditions the parameter value must satisfy.
+ /// The produced operation parameter.
+ ///
+ /// The identifier is null.
+ /// or
+ /// The type is null.
+ ///
+ public static GeometryStreamParameter CreateOptionalParameter(String identifier, String name, String remarks, String[] aliases, T defaultValue, params Predicate[] conditions)
+ {
+ return new GeometryStreamParameter(identifier, name, remarks, aliases, typeof(T), true, defaultValue, conditions);
+ }
+
+
+ ///
+ /// Creates an optional .
+ ///
+ /// The identifier.
+ /// The name.
+ /// The type declaration of the parameter.
+ /// The produced operation parameter.
+ ///
+ /// The identifier is null.
+ /// or
+ /// The type is null.
+ ///
+ public static GeometryStreamParameter CreateOptionalParameter(String identifier, String name, String remarks, Type type)
+ {
+ return new GeometryStreamParameter(identifier, name, remarks, null, type, true, null, null);
+ }
+
+ ///
+ /// Creates an optional .
+ ///
+ /// The identifier.
+ /// The name.
+ /// The type declaration of the parameter.
+ /// The default value of the parameter.
+ /// The produced operation parameter.
+ ///
+ /// The identifier is null.
+ /// or
+ /// The type is null.
+ ///
+ public static GeometryStreamParameter CreateOptionalParameter(String identifier, String name, String remarks, Type type, Object defaultValue)
+ {
+ return new GeometryStreamParameter(identifier, name, remarks, null, type, true, defaultValue, null);
+ }
+
+ ///
+ /// Creates an optional .
+ ///
+ /// The identifier.
+ /// The name.
+ /// The remarks.
+ /// The aliases.
+ /// The type declaration of the parameter.
+ /// The produced operation parameter.
+ ///
+ /// The identifier is null.
+ /// or
+ /// The type is null.
+ ///
+ public static GeometryStreamParameter CreateOptionalParameter(String identifier, String name, String remarks, String[] aliases, Type type)
+ {
+ return new GeometryStreamParameter(identifier, name, remarks, aliases, type, true, null, null);
+ }
+
+ ///
+ /// Creates an optional .
+ ///
+ /// The identifier.
+ /// The name.
+ /// The remarks.
+ /// The aliases.
+ /// The type declaration of the parameter.
+ /// The default value of the parameter.
+ /// The produced operation parameter.
+ ///
+ /// The identifier is null.
+ /// or
+ /// The type is null.
+ ///
+ public static GeometryStreamParameter CreateOptionalParameter(String identifier, String name, String remarks, String[] aliases, Type type, Object defaultValue)
+ {
+ return new GeometryStreamParameter(identifier, name, remarks, aliases, type, true, defaultValue, null);
+ }
+
+ ///
+ /// Creates an optional .
+ ///
+ /// The identifier.
+ /// The name.
+ /// The type declaration of the parameter.
+ /// The conditions the parameter value must satisfy.
+ /// The produced operation parameter.
+ ///
+ /// The identifier is null.
+ /// or
+ /// The type is null.
+ ///
+ public static GeometryStreamParameter CreateOptionalParameter(String identifier, String name, String remarks, Type type, params Predicate[] conditions)
+ {
+ return new GeometryStreamParameter(identifier, name, remarks, null, type, true, null, conditions);
+ }
+
+ ///
+ /// Creates an optional .
+ ///
+ /// The identifier.
+ /// The name.
+ /// The type declaration of the parameter.
+ /// The default value of the parameter.
+ /// The conditions the parameter value must satisfy.
+ /// The produced operation parameter.
+ ///
+ /// The identifier is null.
+ /// or
+ /// The type is null.
+ ///
+ public static GeometryStreamParameter CreateOptionalParameter(String identifier, String name, String remarks, Type type, Object defaultValue, params Predicate[] conditions)
+ {
+ return new GeometryStreamParameter(identifier, name, remarks, null, type, true, defaultValue, conditions);
+ }
+
+ ///
+ /// Creates an optional .
+ ///
+ /// The identifier.
+ /// The name.
+ /// The remarks.
+ /// The aliases.
+ /// The type declaration of the parameter.
+ /// The conditions the parameter value must satisfy.
+ /// The produced operation parameter.
+ ///
+ /// The identifier is null.
+ /// or
+ /// The type is null.
+ ///
+ public static GeometryStreamParameter CreateOptionalParameter(String identifier, String name, String remarks, String[] aliases, Type type, params Predicate[] conditions)
+ {
+ return new GeometryStreamParameter(identifier, name, remarks, aliases, type, true, null, conditions);
+ }
+
+ ///
+ /// Creates an optional .
+ ///
+ /// The identifier.
+ /// The name.
+ /// The remarks.
+ /// The aliases.
+ /// The type declaration of the parameter.
+ /// The default value of the parameter.
+ /// The conditions the parameter value must satisfy.
+ /// The produced operation parameter.
+ ///
+ /// The identifier is null.
+ /// or
+ /// The type is null.
+ ///
+ public static GeometryStreamParameter CreateOptionalParameter(String identifier, String name, String remarks, String[] aliases, Type type, Object defaultValue, params Predicate[] conditions)
+ {
+ return new GeometryStreamParameter(identifier, name, remarks, aliases, type, true, defaultValue, conditions);
+ }
+
+ ///
+ /// Creates a required .
+ ///
+ /// The type of the parameter.
+ /// The identifier.
+ /// The name.
+ /// The remarks.
+ /// The produced operation parameter.
+ ///
+ /// The identifier is null.
+ /// or
+ /// The type is null.
+ ///
+ public static GeometryStreamParameter CreateRequiredParameter(String identifier, String name, String remarks)
+ {
+ return new GeometryStreamParameter(identifier, name, remarks, null, typeof(T), false, null, null);
+ }
+
+ ///
+ /// Creates a required .
+ ///
+ /// The type of the parameter.
+ /// The identifier.
+ /// The name.
+ /// The remarks.
+ /// The default value of the parameter.
+ /// The produced operation parameter.
+ ///
+ /// The identifier is null.
+ /// or
+ /// The type is null.
+ ///
+ public static GeometryStreamParameter CreateRequiredParameter(String identifier, String name, String remarks, T defaultValue)
+ {
+ return new GeometryStreamParameter(identifier, name, remarks, null, typeof(T), false, defaultValue, null);
+ }
+
+ ///
+ /// Creates a required .
+ ///
+ /// The type of the parameter.
+ /// The identifier.
+ /// The name.
+ /// The remarks.
+ /// The aliases.
+ /// The produced operation parameter.
+ ///
+ /// The identifier is null.
+ /// or
+ /// The type is null.
+ ///
+ public static GeometryStreamParameter CreateRequiredParameter(String identifier, String name, String remarks, String[] aliases)
+ {
+ return new GeometryStreamParameter(identifier, name, remarks, aliases, typeof(T), false, null, null);
+ }
+
+ ///
+ /// Creates a required .
+ ///
+ /// The type of the parameter.
+ /// The identifier.
+ /// The name.
+ /// The remarks.
+ /// The aliases.
+ /// The default value of the parameter.
+ /// The produced operation parameter.
+ ///
+ /// The identifier is null.
+ /// or
+ /// The type is null.
+ ///
+ public static GeometryStreamParameter CreateRequiredParameter(String identifier, String name, String remarks, String[] aliases, T defaultValue)
+ {
+ return new GeometryStreamParameter(identifier, name, remarks, aliases, typeof(T), false, defaultValue, null);
+ }
+
+ ///
+ /// Creates a required .
+ ///
+ /// The type of the parameter.
+ /// The identifier.
+ /// The name.
+ /// The remarks.
+ /// The conditions the parameter value must satisfy.
+ /// The produced operation parameter.
+ ///
+ /// The identifier is null.
+ /// or
+ /// The type is null.
+ ///
+ public static GeometryStreamParameter CreateRequiredParameter(String identifier, String name, String remarks, params Predicate[] conditions)
+ {
+ return new GeometryStreamParameter(identifier, name, remarks, null, typeof(T), false, null, conditions);
+ }
+
+ ///
+ /// Creates a required .
+ ///
+ /// The type of the parameter.
+ /// The identifier.
+ /// The name.
+ /// The remarks.
+ /// The default value of the parameter.
+ /// The conditions the parameter value must satisfy.
+ /// The produced operation parameter.
+ ///
+ /// The identifier is null.
+ /// or
+ /// The type is null.
+ ///
+ public static GeometryStreamParameter CreateRequiredParameter(String identifier, String name, String remarks, T defaultValue, params Predicate[] conditions)
+ {
+ return new GeometryStreamParameter(identifier, name, remarks, null, typeof(T), false, defaultValue, conditions);
+ }
+
+ ///
+ /// Creates a required .
+ ///
+ /// The type of the parameter.
+ /// The identifier.
+ /// The name.
+ /// The remarks.
+ /// The aliases.
+ /// The conditions the parameter value must satisfy.
+ /// The produced operation parameter.
+ ///
+ /// The identifier is null.
+ /// or
+ /// The type is null.
+ ///
+ public static GeometryStreamParameter CreateRequiredParameter(String identifier, String name, String remarks, String[] aliases, params Predicate[] conditions)
+ {
+ return new GeometryStreamParameter(identifier, name, remarks, aliases, typeof(T), false, null, conditions);
+ }
+
+ ///
+ /// Creates a required .
+ ///
+ /// The type of the parameter.
+ /// The identifier.
+ /// The name.
+ /// The remarks.
+ /// The aliases.
+ /// The default value of the parameter.
+ /// The conditions the parameter value must satisfy.
+ /// The produced operation parameter.
+ ///
+ /// The identifier is null.
+ /// or
+ /// The type is null.
+ ///
+ public static GeometryStreamParameter CreateRequiredParameter(String identifier, String name, String remarks, String[] aliases, T defaultValue, params Predicate[] conditions)
+ {
+ return new GeometryStreamParameter(identifier, name, remarks, aliases, typeof(T), false, defaultValue, conditions);
+ }
+
+ ///
+ /// Creates a required .
+ ///
+ /// The identifier.
+ /// The name.
+ /// The remarks.
+ /// The type declaration of the parameter.
+ /// The produced operation parameter.
+ ///
+ /// The identifier is null.
+ /// or
+ /// The type is null.
+ ///
+ public static GeometryStreamParameter CreateRequiredParameter(String identifier, String name, String remarks, Type type)
+ {
+ return new GeometryStreamParameter(identifier, name, remarks, null, type, false, null, null);
+ }
+
+ ///
+ /// Creates a required .
+ ///
+ /// The identifier.
+ /// The name.
+ /// The remarks.
+ /// The type declaration of the parameter.
+ /// The default value of the parameter.
+ /// The produced operation parameter.
+ ///
+ /// The identifier is null.
+ /// or
+ /// The type is null.
+ ///
+ public static GeometryStreamParameter CreateRequiredParameter(String identifier, String name, String remarks, Type type, Object defaultValue)
+ {
+ return new GeometryStreamParameter(identifier, name, remarks, null, type, false, defaultValue, null);
+ }
+
+ ///
+ /// Creates a required .
+ ///
+ /// The identifier.
+ /// The name.
+ /// The remarks.
+ /// The aliases.
+ /// The type declaration of the parameter.
+ /// The produced operation parameter.
+ ///
+ /// The identifier is null.
+ /// or
+ /// The type is null.
+ ///
+ public static GeometryStreamParameter CreateRequiredParameter(String identifier, String name, String remarks, String[] aliases, Type type)
+ {
+ return new GeometryStreamParameter(identifier, name, remarks, aliases, type, false, null, null);
+ }
+
+ ///
+ /// Creates a required .
+ ///
+ /// The identifier.
+ /// The name.
+ /// The remarks.
+ /// The aliases.
+ /// The default value of the parameter.
+ /// The type declaration of the parameter.
+ /// The produced operation parameter.
+ ///
+ /// The identifier is null.
+ /// or
+ /// The type is null.
+ ///
+ public static GeometryStreamParameter CreateRequiredParameter(String identifier, String name, String remarks, String[] aliases, Type type, Object defaultValue)
+ {
+ return new GeometryStreamParameter(identifier, name, remarks, aliases, type, false, defaultValue, null);
+ }
+
+ ///
+ /// Creates a required .
+ ///
+ /// The identifier.
+ /// The name.
+ /// The remarks.
+ /// The type declaration of the parameter.
+ /// The conditions the parameter value must satisfy.
+ /// The produced operation parameter.
+ ///
+ /// The identifier is null.
+ /// or
+ /// The type is null.
+ ///
+ public static GeometryStreamParameter CreateRequiredParameter(String identifier, String name, String remarks, Type type, params Predicate[] conditions)
+ {
+ return new GeometryStreamParameter(identifier, name, remarks, null, type, false, null, conditions);
+ }
+
+ ///
+ /// Creates a required .
+ ///
+ /// The identifier.
+ /// The name.
+ /// The remarks.
+ /// The type declaration of the parameter.
+ /// The default value of the parameter.
+ /// The conditions the parameter value must satisfy.
+ /// The produced operation parameter.
+ ///
+ /// The identifier is null.
+ /// or
+ /// The type is null.
+ ///
+ public static GeometryStreamParameter CreateRequiredParameter(String identifier, String name, String remarks, Type type, Object defaultValue, params Predicate[] conditions)
+ {
+ return new GeometryStreamParameter(identifier, name, remarks, null, type, false, defaultValue, conditions);
+ }
+
+ ///
+ /// Creates a required .
+ ///
+ /// The identifier.
+ /// The name.
+ /// The remarks.
+ /// The aliases.
+ /// The type declaration of the parameter.
+ /// The conditions the parameter value must satisfy.
+ /// The produced operation parameter.
+ ///
+ /// The identifier is null.
+ /// or
+ /// The type is null.
+ ///
+ public static GeometryStreamParameter CreateRequiredParameter(String identifier, String name, String remarks, String[] aliases, Type type, params Predicate[] conditions)
+ {
+ return new GeometryStreamParameter(identifier, name, remarks, aliases, type, false, null, conditions);
+ }
+
+ ///
+ /// Creates a required .
+ ///
+ /// The identifier.
+ /// The name.
+ /// The remarks.
+ /// The aliases.
+ /// The type declaration of the parameter.
+ /// The default value of the parameter.
+ /// The conditions the parameter value must satisfy.
+ /// The produced operation parameter.
+ ///
+ /// The identifier is null.
+ /// or
+ /// The type is null.
+ ///
+ public static GeometryStreamParameter CreateRequiredParameter(String identifier, String name, String remarks, String[] aliases, Type type, Object defaultValue, params Predicate[] conditions)
+ {
+ return new GeometryStreamParameter(identifier, name, remarks, aliases, type, false, defaultValue, conditions);
+ }
+
+ #endregion
+ }
+}
diff --git a/src/IO/GeometryStreamParameters.cs b/src/IO/GeometryStreamParameters.cs
new file mode 100644
index 0000000..4ebb333
--- /dev/null
+++ b/src/IO/GeometryStreamParameters.cs
@@ -0,0 +1,138 @@
+//
+// Copyright 2016-2021 Roberto Giachetta. Licensed under the
+// Educational Community License, Version 2.0 (the "License"); you may
+// not use this file except in compliance with the License. You may
+// obtain a copy of the License at
+// http://opensource.org/licenses/ECL-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an "AS IS"
+// BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
+// or implied. See the License for the specific language governing
+// permissions and limitations under the License.
+//
+
+using System;
+using System.Collections.Generic;
+using System.Linq;
+
+namespace AEGIS.IO
+{
+ ///
+ /// Represents a collection of known instances.
+ ///
+ [IdentifiedObjectCollection(typeof(GeometryStreamParameter))]
+ public class GeometryStreamParameters
+ {
+ #region Query fields
+
+ private static GeometryStreamParameter[] _all;
+
+ #endregion
+
+ #region Query properties
+
+ ///
+ /// Gets all instances within the collection.
+ ///
+ /// A read-only list containing all instances within the collection.
+ public static IList All
+ {
+ get
+ {
+ if (_all == null)
+ _all = typeof(GeometryStreamParameters).GetProperties().
+ Where(property => property.Name != "All").
+ Select(property => property.GetValue(null, null) as GeometryStreamParameter).
+ ToArray();
+ return Array.AsReadOnly(_all);
+ }
+ }
+
+ #endregion
+
+ #region Query methods
+
+ ///
+ /// Returns all instances matching a specified identifier.
+ ///
+ /// The identifier.
+ /// A list containing the instances that match the specified identifier.
+ public static IList FromIdentifier(String identifier)
+ {
+ if (identifier == null)
+ return null;
+
+ return All.Where(obj => System.Text.RegularExpressions.Regex.IsMatch(obj.Identifier, identifier)).ToList().AsReadOnly();
+ }
+
+ ///
+ /// Returns all instances matching a specified name.
+ ///
+ /// The name.
+ /// A list containing the instances that match the specified name.
+ public static IList FromName(String name)
+ {
+ if (name == null)
+ return null;
+
+ return All.Where(obj => System.Text.RegularExpressions.Regex.IsMatch(obj.Name, name) ||
+ obj.Aliases != null && obj.Aliases.Any(alias => System.Text.RegularExpressions.Regex.IsMatch(alias, name, System.Text.RegularExpressions.RegexOptions.IgnoreCase))).ToList().AsReadOnly();
+ }
+
+ #endregion
+
+ #region Private static fields
+
+ private static GeometryStreamParameter _geometryFactory;
+ private static GeometryStreamParameter _geometryFactoryType;
+ private static GeometryStreamParameter _bufferingMode;
+
+ #endregion
+
+ #region Public static fields
+
+ ///
+ /// Geometry factory.
+ ///
+ public static GeometryStreamParameter GeometryFactory
+ {
+ get
+ {
+ return _geometryFactory ?? (_geometryFactory =
+ GeometryStreamParameter.CreateOptionalParameter("AEGIS::620002", "Geometry factory",
+ "The geometry factory used to produce the instances read from the specified format. If geometry factory is specified, the reference system of the factory is used instead of the reference system provided by the source."));
+ }
+ }
+
+ ///
+ /// Geometry factory type.
+ ///
+ public static GeometryStreamParameter GeometryFactoryType
+ {
+ get
+ {
+ return _geometryFactoryType ?? (_geometryFactoryType =
+ GeometryStreamParameter.CreateOptionalParameter("AEGIS::620001", "Geometry factory type",
+ "The type of the geometry factory used to produce the instances read from the specified format. If geometry factory type is specified, an instance of this type will be used with the reference system provided by the source.",
+ Conditions.Implements()));
+ }
+ }
+
+ ///
+ /// Buffering mode.
+ ///
+ public static GeometryStreamParameter BufferingMode
+ {
+ get
+ {
+ return _bufferingMode ?? (_bufferingMode =
+ GeometryStreamParameter.CreateOptionalParameter("AEGIS::620008", "buffering mode",
+ "The buffering mode.",
+ IO.BufferingMode.None));
+ }
+ }
+
+ #endregion
+ }
+}
diff --git a/src/IO/GeometryStreamReader.cs b/src/IO/GeometryStreamReader.cs
new file mode 100644
index 0000000..5b23822
--- /dev/null
+++ b/src/IO/GeometryStreamReader.cs
@@ -0,0 +1,621 @@
+//
+// Copyright 2016-2021 Roberto Giachetta. Licensed under the
+// Educational Community License, Version 2.0 (the "License"); you may
+// not use this file except in compliance with the License. You may
+// obtain a copy of the License at
+// http://opensource.org/licenses/ECL-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an "AS IS"
+// BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
+// or implied. See the License for the specific language governing
+// permissions and limitations under the License.
+//
+
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using AEGIS.Collections;
+using AEGIS.IO.Utilities;
+using AEGIS.Storage;
+using AEGIS.Storage.FileSystems;
+
+namespace AEGIS.IO
+{
+ ///
+ /// Represents a base type for stream based geometry reading.
+ ///
+ public abstract class GeometryStreamReader : IDisposable
+ {
+ #region Protected constant fields
+
+ ///
+ /// Exception message in case the path is null. This field is constant.
+ ///
+ protected const String MessagePathIsNull = "The path is null.";
+
+ ///
+ /// Exception message in case the path is empty, or consists only of whitespace characters. This field is constant.
+ ///
+ protected const String MessagePathIsEmpty = "The path is empty, or consists only of whitespace characters.";
+
+ ///
+ /// Exception message in case the path is in an invalid format. This field is constant.
+ ///
+ protected const String MessagePathIsInInvalidFormat = "The path is in an invalid format.";
+
+ ///
+ /// Exception message in case the stream is null. This field is constant.
+ ///
+ protected const String MessageStreamIsNull = "The stream is null.";
+
+ ///
+ /// Exception message in case the format is null. This field is constant.
+ ///
+ protected const String MessageFormatIsNull = "The format is null.";
+
+ ///
+ /// Exception message in case the format requires parameters which are not specified. This field is constant.
+ ///
+ protected const String MessageParametersNull = "The format requires parameters which are not specified.";
+
+ ///
+ /// Exception message in case the parameters do not contain a required parameter value. This field is constant.
+ ///
+ protected const String MessageParameterMissing = "The parameters do not contain a required parameter value ({0}).";
+
+ ///
+ /// Exception message in case the type of a parameter value does not match the type specified by the method. This field is constant.
+ ///
+ protected const String MessageParameterTypeError = "The type of a parameter value ({0}) does not match the type specified by the method.";
+
+ ///
+ /// Exception message in case the parameter value does not satisfy the conditions of the parameter. This field is constant.
+ ///
+ protected const String MessageParameterConditionError = "The parameter value ({0}) does not satisfy the conditions of the parameter.";
+
+ ///
+ /// Exception message in case error occurred during stream opening. This field is constant.
+ ///
+ protected const String MessageContentOpenError = "Error occurred during stream opening.";
+
+ ///
+ /// Exception message in case error occurred during stream reading. This field is constant.
+ ///
+ protected const String MessageContentReadError = "Error occurred during stream reading.";
+
+ ///
+ /// Exception message in case the stream content is in invalid. This field is constant.
+ ///
+ protected const String MessageContentInvalid = "The stream content is in invalid.";
+
+ ///
+ /// Exception message in case the stream header is in invalid. This field is constant.
+ ///
+ protected const String MessageHeaderInvalid = "The stream header is in invalid.";
+
+ #endregion
+
+ #region Private fields
+
+ ///
+ /// Defines an empty collection of parameters. This field is read-only.
+ ///
+ private static readonly Dictionary EmptyParameters = new Dictionary();
+
+ ///
+ /// The parameters of the reader. This field is read-only.
+ ///
+ private readonly IDictionary _parameters;
+
+ ///
+ /// The source stream.
+ ///
+ private Stream _sourceStream;
+
+ ///
+ /// The factory used for producing geometries.
+ ///
+ private IGeometryFactory _factory;
+
+ ///
+ /// A value indicating whether this instance is disposed.
+ ///
+ private Boolean _disposed;
+
+ ///
+ /// A value indicating whether to dispose the underlying stream.
+ ///
+ private Boolean _disposeSourceStream;
+
+ ///
+ /// The buffering mode.
+ ///
+ private BufferingMode _bufferingMode;
+
+ #endregion
+
+ #region Protected fields
+
+ ///
+ /// The underlying stream.
+ ///
+ protected readonly Stream _baseStream;
+
+ #endregion
+
+ #region Public properties
+
+ ///
+ /// Gets the factory used for geometry production.
+ ///
+ /// The factory used by the reader for geometry production.
+ public IGeometryFactory Factory { get { return _factory; } }
+
+ ///
+ /// Gets the format of the geometry stream.
+ ///
+ /// The format of the geometry stream.
+ public GeometryStreamFormat Format { get; private set; }
+
+ ///
+ /// Gets the parameters of the reader.
+ ///
+ /// The parameters of the reader stored as key/value pairs.
+ public IReadOnlyDictionary Parameters { get { return (_parameters != null ? _parameters : EmptyParameters).AsReadOnly(); } }
+
+ ///
+ /// Gets the path of the data.
+ ///
+ /// The full path of the data.
+ public Uri Path { get; private set; }
+
+ ///
+ /// Gets the underlying stream.
+ ///
+ /// The underlying stream.
+ public Stream BaseStream { get { return _baseStream; } }
+
+ ///
+ /// Gets a value that indicates whether the current stream position is at the end of the stream.
+ ///
+ /// true if the current stream position is at the end of the stream; otherwise, false .
+ /// Object is disposed.
+ public virtual Boolean EndOfStream
+ {
+ get
+ {
+ if (_disposed)
+ throw new ObjectDisposedException(GetType().FullName);
+ return GetEndOfStream();
+ }
+ }
+
+ #endregion
+
+ #region Constructor and destructor
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// The file path to be read.
+ /// The format of the stream reader.
+ /// The parameters of the reader for the specific stream.
+ ///
+ /// The path is null.
+ /// or
+ /// The format is null.
+ /// or
+ /// The format requires parameters which are not specified.
+ ///
+ ///
+ /// The path is empty, or consists only of whitespace characters.
+ /// or
+ /// The path is in an invalid format.
+ /// or
+ /// The parameters do not contain a required parameter value.
+ /// or
+ /// The type of a parameter value does not match the type specified by the format.
+ /// or
+ /// The parameter value does not satisfy the conditions of the parameter.
+ ///
+ /// Exception occurred during stream opening.
+ protected GeometryStreamReader(String path, GeometryStreamFormat format, IDictionary parameters)
+ : this(ResolvePath(path), format, parameters)
+ { }
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// The file path to be read.
+ /// The format of the stream reader.
+ /// The parameters of the reader for the specific stream.
+ ///
+ /// The path is null.
+ /// or
+ /// The format is null.
+ /// or
+ /// The format requires parameters which are not specified.
+ ///
+ ///
+ /// The parameters do not contain a required parameter value.
+ /// or
+ /// The type of a parameter value does not match the type specified by the format.
+ ///
+ /// Exception occurred during stream opening.
+ protected GeometryStreamReader(Uri path, GeometryStreamFormat format, IDictionary parameters)
+ : this(ResolveStream(path), format, parameters)
+ {
+ Path = path;
+ _disposeSourceStream = true;
+ }
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// The stream to be read.
+ /// The format of the stream reader.
+ /// The parameters of the reader for the specific stream.
+ ///
+ /// The stream is null.
+ /// or
+ /// The format is null.
+ /// or
+ /// The format requires parameters which are not specified.
+ ///
+ ///
+ /// The parameters do not contain a required parameter value.
+ /// or
+ /// The type of a parameter value does not match the type specified by the format.
+ ///
+ protected GeometryStreamReader(Stream stream, GeometryStreamFormat format, IDictionary parameters)
+ : this(format, parameters)
+ {
+ if (stream == null)
+ throw new ArgumentNullException("stream", MessageStreamIsNull);
+
+ // store parameters
+ _disposeSourceStream = false;
+ _sourceStream = stream;
+
+ // apply buffering
+ switch (_bufferingMode)
+ {
+ case BufferingMode.Minimal:
+ _baseStream = new ProxyStream(stream, ProxyStreamOptions.ForceProxy | ProxyStreamOptions.SingleAccess);
+ break;
+ case BufferingMode.Maximal:
+ _baseStream = new MemoryBufferedStream(_sourceStream);
+ break;
+ default:
+ _baseStream = _sourceStream;
+ break;
+ }
+ }
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// The format of the stream reader.
+ /// The parameters of the reader.
+ ///
+ /// The format is null.
+ /// or
+ /// The format requires parameters which are not specified.
+ ///
+ ///
+ /// The parameters do not contain a required parameter value.
+ /// or
+ /// The type of a parameter value does not match the type specified by the format.
+ ///
+ protected GeometryStreamReader(GeometryStreamFormat format, IDictionary parameters)
+ {
+ if (format == null)
+ throw new ArgumentNullException("format", MessageFormatIsNull);
+ if (parameters == null && format.Parameters != null && format.Parameters.Length > 0)
+ throw new ArgumentNullException("parameters", MessageParametersNull);
+
+ if (parameters != null && format.Parameters != null)
+ {
+ foreach (GeometryStreamParameter parameter in format.Parameters)
+ {
+ // check parameter existence
+ if (!parameter.IsOptional && (!parameters.ContainsKey(parameter) || parameters[parameter] == null))
+ throw new ArgumentException(String.Format(MessageParameterMissing, parameter.Name), "parameters");
+
+ if (parameters.ContainsKey(parameter))
+ {
+ // check parameter type
+ if (!(parameter.Type.GetInterfaces().Contains(typeof(IConvertible)) && parameters[parameter] is IConvertible) &&
+ !parameter.Type.Equals(parameters[parameter].GetType()) &&
+ !parameters[parameter].GetType().IsSubclassOf(parameter.Type) &&
+ !parameters[parameter].GetType().GetInterfaces().Contains(parameter.Type))
+ throw new ArgumentException(String.Format(MessageParameterTypeError, parameter.Name), "parameters");
+
+ // check parameter value
+ if (!parameter.IsValid(parameters[parameter]))
+ throw new ArgumentException(String.Format(MessageParameterConditionError, parameter.Name), "parameters");
+ }
+ }
+ }
+
+ // store parameters
+ Format = format;
+ _parameters = parameters;
+ _disposeSourceStream = true;
+ _disposed = false;
+ _bufferingMode = ResolveParameter(GeometryStreamParameters.BufferingMode);
+
+ // resolve factory or factory type
+ if (IsProvidedParameter(GeometryStreamParameters.GeometryFactory))
+ _factory = ResolveParameter(GeometryStreamParameters.GeometryFactory);
+ }
+
+ ///
+ /// Finalizes an instance of the class.
+ ///
+ ~GeometryStreamReader()
+ {
+ Dispose(false);
+ }
+
+ #endregion
+
+ #region Public methods
+
+ ///
+ /// Reads a single geometry from the data stream.
+ ///
+ /// The geometry read from the data stream.
+ /// Object is disposed.
+ /// Exception occurred during stream reading.
+ public IGeometry Read()
+ {
+ if (_disposed)
+ throw new ObjectDisposedException(GetType().FullName);
+
+ try
+ {
+ return ApplyReadGeometry();
+ }
+ catch (Exception ex)
+ {
+ throw new InvalidDataException(MessageContentReadError, ex);
+ }
+ }
+
+ ///
+ /// Reads a number of geometries from the data stream.
+ ///
+ /// The number of geometries to read.
+ /// A list containing the geometries read from the data stream.
+ /// Object is disposed.
+ /// Exception occurred during stream reading.
+ public IList Read(Int32 count)
+ {
+ if (_disposed)
+ throw new ObjectDisposedException(GetType().FullName);
+
+ try
+ {
+ List result = new List(count);
+ for (Int32 i = 0; i < count && !EndOfStream; i++)
+ {
+ result.Add(ApplyReadGeometry());
+ }
+ return result;
+ }
+ catch (Exception ex)
+ {
+ throw new IOException(MessageContentReadError, ex);
+ }
+ }
+
+ ///
+ /// Reads all geometries from the data stream.
+ ///
+ /// A list containing the geometries read from the data stream.
+ /// Object is disposed.
+ /// Exception occurred during stream reading.
+ public IList ReadToEnd()
+ {
+ if (_disposed)
+ throw new ObjectDisposedException(GetType().FullName);
+
+ try
+ {
+ List result = new List();
+ while (!EndOfStream)
+ {
+ result.Add(ApplyReadGeometry());
+ }
+ return result.ToArray();
+ }
+ catch (Exception ex)
+ {
+ throw new IOException("Exception occurred during stream reading.", ex);
+ }
+ }
+
+ ///
+ /// Closes the reader and the underlying stream, and releases any system resources associated with the reader.
+ ///
+ public virtual void Close()
+ {
+ Dispose();
+ }
+
+ #endregion
+
+ #region IDisposable methods
+
+ ///
+ /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.
+ ///
+ public void Dispose()
+ {
+ if (_disposed)
+ return;
+
+ Dispose(true);
+ GC.SuppressFinalize(this);
+ }
+
+ #endregion
+
+ #region Protected methods
+
+ ///
+ /// Returns a value indicating whether the end of the stream is reached.
+ ///
+ /// true if the end of the stream is reached; otherwise, false .
+ protected abstract Boolean GetEndOfStream();
+
+ ///
+ /// Apply the read operation for a geometry.
+ ///
+ /// The geometry read from the stream.
+ protected abstract IGeometry ApplyReadGeometry();
+
+ ///
+ /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.
+ ///
+ /// A value indicating whether disposing is performed on the object.
+ protected virtual void Dispose(Boolean disposing)
+ {
+ _disposed = true;
+
+ if (disposing)
+ {
+ if (_baseStream != null)
+ {
+ switch (_bufferingMode)
+ {
+ case BufferingMode.Minimal:
+ case BufferingMode.Maximal:
+ _baseStream.Dispose();
+ break;
+ }
+
+ if (_disposeSourceStream)
+ _sourceStream.Dispose();
+ }
+ }
+ }
+
+ ///
+ /// Resolves the specified parameter.
+ ///
+ /// The type of the parameter.
+ /// The parameter.
+ /// The specified parameter value or the default value if none specified.
+ protected T ResolveParameter(GeometryStreamParameter parameter)
+ {
+ if (_parameters != null && _parameters.ContainsKey(parameter) && _parameters[parameter] is T)
+ return (T)_parameters[parameter];
+
+ return (T)parameter.DefaultValue;
+ }
+
+ ///
+ /// Determines whether the specified parameter is provided.
+ ///
+ /// The parameter.
+ /// true if the parameter is provided; otherwise, false .
+ protected Boolean IsProvidedParameter(GeometryStreamParameter parameter)
+ {
+ return _parameters != null && _parameters.ContainsKey(parameter);
+ }
+
+ ///
+ /// Opens the file on the specified path.
+ ///
+ /// The path of the file.
+ /// The stream of the file.
+ protected Stream OpenPath(String path)
+ {
+ return OpenPath(ResolvePath(path));
+ }
+
+ ///
+ /// Opens the file on the specified path.
+ ///
+ /// The path of the file.
+ /// The stream of the file.
+ protected Stream OpenPath(Uri path)
+ {
+ IFileSystem fileSystem = FileSystemContainer.GetFileSystemForScheme(path.Scheme);
+ Stream stream;
+
+ if (path.IsAbsoluteUri)
+ stream = fileSystem.OpenFile(path.AbsolutePath, FileMode.Open, FileAccess.Read);
+ else
+ stream = fileSystem.OpenFile(path.OriginalString, FileMode.Open, FileAccess.Read);
+
+ // apply buffering
+ switch (_bufferingMode)
+ {
+ case BufferingMode.Minimal:
+ return new ProxyStream(stream, true, ProxyStreamOptions.ForceProxy | ProxyStreamOptions.SingleAccess);
+ case BufferingMode.Maximal:
+ return new MemoryBufferedStream(stream, true);
+ default:
+ return stream;
+ }
+ }
+
+ #endregion
+
+ #region Private methods
+
+ ///
+ /// Resolves the path.
+ ///
+ /// The path string.
+ /// The path URI.
+ /// The path is null.
+ ///
+ /// The path is empty, or consists only of whitespace characters.
+ /// or
+ /// The path is in an invalid format.
+ ///
+ private static Uri ResolvePath(String path)
+ {
+ Uri pathUri;
+
+ if (path == null)
+ throw new ArgumentNullException("path", MessagePathIsNull);
+ if (String.IsNullOrEmpty(path))
+ throw new ArgumentException(MessagePathIsEmpty, "path");
+ if (!Uri.TryCreate(path, UriKind.RelativeOrAbsolute, out pathUri))
+ throw new ArgumentException(MessagePathIsInInvalidFormat, "path");
+
+ return pathUri;
+ }
+
+ ///
+ /// Resolves the stream.
+ ///
+ /// The path.
+ /// The stream.
+ /// Exception occurred during stream opening.
+ private static Stream ResolveStream(Uri path)
+ {
+ try
+ {
+ IFileSystem fileSystem = FileSystemContainer.GetFileSystemForScheme(path.Scheme);
+ if (path.IsAbsoluteUri)
+ return fileSystem.OpenFile(path.AbsolutePath, FileMode.Open, FileAccess.Read);
+ else
+ return fileSystem.OpenFile(path.OriginalString, FileMode.Open, FileAccess.Read);
+ }
+ catch (Exception ex)
+ {
+ throw new IOException(MessageContentOpenError, ex);
+ }
+ }
+
+ #endregion
+ }
+}
diff --git a/src/IO/GeometryStreamWriter.cs b/src/IO/GeometryStreamWriter.cs
new file mode 100644
index 0000000..757cb2a
--- /dev/null
+++ b/src/IO/GeometryStreamWriter.cs
@@ -0,0 +1,579 @@
+//
+// Copyright 2016-2021 Roberto Giachetta. Licensed under the
+// Educational Community License, Version 2.0 (the "License"); you may
+// not use this file except in compliance with the License. You may
+// obtain a copy of the License at
+// http://opensource.org/licenses/ECL-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an "AS IS"
+// BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
+// or implied. See the License for the specific language governing
+// permissions and limitations under the License.
+//
+
+using System;
+using System.Collections.Generic;
+using System.Collections.ObjectModel;
+using System.IO;
+using System.Linq;
+using AEGIS.IO.Utilities;
+using AEGIS.Storage;
+using AEGIS.Storage.FileSystems;
+
+namespace AEGIS.IO
+{
+ ///
+ /// Represents a base type for stream based geometry writing.
+ ///
+ public abstract class GeometryStreamWriter : IDisposable
+ {
+ #region Protected constant fields
+
+ ///
+ /// Exception message in case the path is null. This field is constant.
+ ///
+ protected const String MessagePathIsNull = "The path is null.";
+
+ ///
+ /// Exception message in case the path is empty, or consists only of whitespace characters. This field is constant.
+ ///
+ protected const String MessagePathIsEmpty = "The path is empty, or consists only of whitespace characters.";
+
+ ///
+ /// Exception message in case the path is in an invalid format. This field is constant.
+ ///
+ protected const String MessagePathIsInInvalidFormat = "The path is in an invalid format.";
+
+ ///
+ /// Exception message in case the stream is null. This field is constant.
+ ///
+ protected const String MessageStreamIsNull = "The stream is null.";
+
+ ///
+ /// Exception message in case the format is null. This field is constant.
+ ///
+ protected const String MessageFormatIsNull = "The format is null.";
+
+ ///
+ /// Exception message in case the geometry is null. This field is constant.
+ ///
+ protected const String MessageGeometryIsNull = "The geometry is null.";
+
+ ///
+ /// Exception message in case the type of the geometry is not supported. This field is constant.
+ ///
+ protected const String MessageGeometryIsNotSupported = "The type of the geometry is not supported by the format.";
+
+ ///
+ /// Exception message in case one or more of the geometries is not supported by the format. This field is constant.
+ ///
+ protected const String MessageGeometriesAreNotSupported = "One or more of the geometries is not supported by the format.";
+
+ ///
+ /// Exception message in case the format requires parameters which are not specified. This field is constant.
+ ///
+ protected const String MessageParametersNull = "The format requires parameters which are not specified.";
+
+ ///
+ /// Exception message in case the parameters do not contain a required parameter value. This field is constant.
+ ///
+ protected const String MessageParameterMissing = "The parameters do not contain a required parameter value ({0}).";
+
+ ///
+ /// Exception message in case the type of a parameter value does not match the type specified by the method. This field is constant.
+ ///
+ protected const String MessageParameterTypeError = "The type of a parameter value ({0}) does not match the type specified by the method.";
+
+ ///
+ /// Exception message in case the parameter value does not satisfy the conditions of the parameter. This field is constant.
+ ///
+ protected const String MessageParameterConditionError = "The parameter value ({0}) does not satisfy the conditions of the parameter.";
+
+ ///
+ /// Exception message in case error occurred during stream opening. This field is constant.
+ ///
+ protected const String MessageContentOpenError = "Error occurred during stream opening.";
+
+ ///
+ /// Exception message in case error occurred during stream writing. This field is constant.
+ ///
+ protected const String MessageContentWriteError = "Error occurred during stream writing.";
+
+ #endregion
+
+ #region Private fields
+
+ ///
+ /// Defines an empty collection of parameters. This field is read-only.
+ ///
+ private readonly static Dictionary EmptyParameters = new Dictionary();
+
+ ///
+ /// The parameters of the reader. This field is read-only.
+ ///
+ private readonly IDictionary _parameters;
+
+ ///
+ /// The source stream.
+ ///
+ private Stream _sourceStream;
+
+ ///
+ /// The buffering mode.
+ ///
+ private BufferingMode _bufferingMode;
+
+ ///
+ /// A value indicating whether this instance is disposed.
+ ///
+ private Boolean _disposed;
+
+ ///
+ /// A value indicating whether to dispose the underlying stream.
+ ///
+ private Boolean _disposeSourceStream;
+
+ #endregion
+
+ #region Protected fields
+
+ ///
+ /// The underlying stream.
+ ///
+ protected readonly Stream _baseStream;
+
+ #endregion
+
+ #region Public properties
+
+ ///
+ /// Gets the format of the geometry stream.
+ ///
+ /// The format of the geometry stream.
+ public GeometryStreamFormat Format { get; private set; }
+
+ ///
+ /// Gets the parameters of the reader.
+ ///
+ /// The parameters of the reader stored as key/value pairs.
+ public IDictionary Parameters { get { return new ReadOnlyDictionary(_parameters != null ? _parameters : EmptyParameters); } }
+
+ ///
+ /// Gets the path of the data.
+ ///
+ /// The full path of the data.
+ public Uri Path { get; private set; }
+
+ ///
+ /// Gets the underlying stream.
+ ///
+ /// The underlying stream.
+ public Stream BaseStream { get { return _baseStream; } }
+
+ #endregion
+
+ #region Constructors and destructor
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// The file path to be written.
+ /// The format.
+ /// The parameters.
+ ///
+ /// The path is null.
+ /// or
+ /// The format is null.
+ /// or
+ /// The format requires parameters which are not specified.
+ ///
+ ///
+ /// The path is empty, or consists only of whitespace characters.
+ /// or
+ /// The path is in an invalid format.
+ /// or
+ /// The parameters do not contain a required parameter value.
+ /// or
+ /// The type of a parameter value does not match the type specified by the format.
+ /// or
+ /// The parameter value does not satisfy the conditions of the parameter.
+ ///
+ /// Exception occurred during stream opening.
+ protected GeometryStreamWriter(String path, GeometryStreamFormat format, IDictionary parameters)
+ : this(ResolvePath(path), format, parameters)
+ {
+ }
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// The file path to be written.
+ /// The format.
+ /// The parameters.
+ ///
+ /// The path is null.
+ /// or
+ /// The format is null.
+ /// or
+ /// The format requires parameters which are not specified.
+ ///
+ ///
+ /// The path is empty, or consists only of whitespace characters.
+ /// or
+ /// The parameters do not contain a required parameter value.
+ /// or
+ /// The type of a parameter value does not match the type specified by the format.
+ ///
+ /// Exception occurred during stream opening.
+ protected GeometryStreamWriter(Uri path, GeometryStreamFormat format, IDictionary parameters)
+ : this(ResolveStream(path), format, parameters)
+ {
+ Path = path;
+ _disposeSourceStream = true;
+ }
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// The file path to be written.
+ /// The format.
+ /// The parameters.
+ ///
+ /// The stream is null.
+ /// or
+ /// The format is null.
+ /// or
+ /// The format requires parameters which are not specified.
+ ///
+ ///
+ /// The parameters do not contain a required parameter value.
+ /// or
+ /// The type of a parameter value does not match the type specified by the format.
+ ///
+ protected GeometryStreamWriter(Stream stream, GeometryStreamFormat format, IDictionary parameters)
+ {
+ if (stream == null)
+ throw new ArgumentNullException("stream", MessageStreamIsNull);
+ if (format == null)
+ throw new ArgumentNullException("format", MessageFormatIsNull);
+ if (parameters == null && format.Parameters != null && format.Parameters.Length > 0)
+ throw new ArgumentNullException("parameters", MessageParametersNull);
+
+ if (parameters != null && format.Parameters != null)
+ {
+ foreach (GeometryStreamParameter parameter in format.Parameters)
+ {
+ // check parameter existence
+ if (!parameter.IsOptional && (!parameters.ContainsKey(parameter) || parameters[parameter] == null))
+ throw new ArgumentException(String.Format(MessageParameterMissing, parameter.Name), "parameters");
+
+ if (parameters.ContainsKey(parameter))
+ {
+ // check parameter type
+ if (!(parameter.Type.GetInterfaces().Contains(typeof(IConvertible)) && parameters[parameter] is IConvertible) &&
+ !parameter.Type.Equals(parameters[parameter].GetType()) &&
+ !parameters[parameter].GetType().IsSubclassOf(parameter.Type) &&
+ !parameters[parameter].GetType().GetInterfaces().Contains(parameter.Type))
+ throw new ArgumentException(String.Format(MessageParameterTypeError, parameter.Name), "parameters");
+
+ // check parameter value
+ if (!parameter.IsValid(parameters[parameter]))
+ throw new ArgumentException(String.Format(MessageParameterConditionError, parameter.Name), "parameters");
+ }
+ }
+
+ _parameters = new Dictionary(parameters);
+ }
+
+ Format = format;
+ _bufferingMode = ResolveParameter(GeometryStreamParameters.BufferingMode);
+ _disposeSourceStream = false;
+ _disposed = false;
+ _sourceStream = stream;
+
+ // apply buffering
+ switch (_bufferingMode)
+ {
+ case BufferingMode.Minimal:
+ _baseStream = new ProxyStream(_sourceStream, ProxyStreamOptions.ForceProxy | ProxyStreamOptions.SingleAccess);
+ break;
+ case BufferingMode.Maximal:
+ _baseStream = new MemoryBufferedStream(_sourceStream);
+ break;
+ default:
+ _baseStream = _sourceStream;
+ break;
+ }
+ }
+
+ ///
+ /// Finalizes an instance of the class.
+ ///
+ ~GeometryStreamWriter()
+ {
+ Dispose(false);
+ }
+
+ #endregion
+
+ #region Public methods
+
+ ///
+ /// Writes the specified geometry.
+ ///
+ /// The geometry to be written.
+ /// Object is disposed.
+ /// The geometry is null.
+ /// The geometry is not supported by the format.
+ /// Exception occurred during stream writing.
+ public void Write(IGeometry geometry)
+ {
+ if (_disposed)
+ throw new ObjectDisposedException(GetType().FullName);
+
+ if (geometry == null)
+ throw new ArgumentNullException("geometry", MessageGeometryIsNull);
+
+ // required, because IGeometryCollection -> IGeometry conversion is prioritized over
+ // IGeometryCollection -> IEnumerable conversion
+ if (geometry is IEnumerable)
+ {
+ Write(geometry as IEnumerable);
+ return;
+ }
+
+ if (!Format.Supports(geometry))
+ throw new ArgumentException(MessageGeometryIsNotSupported, "geometry");
+
+ try
+ {
+ ApplyWriteGeometry(geometry);
+ }
+ catch (Exception ex)
+ {
+ throw new IOException(MessageContentWriteError, ex);
+ }
+ }
+
+ ///
+ /// Writes the specified geometries.
+ ///
+ /// The geometries to be written.
+ /// Object is disposed.
+ /// The geometry is null.
+ /// One or more of the geometries is not supported by the format.
+ /// Exception occurred during stream writing.
+ public void Write(IEnumerable geometries)
+ {
+ if (_disposed)
+ throw new ObjectDisposedException(GetType().FullName);
+
+ if (geometries == null)
+ throw new ArgumentNullException("geometries", MessageGeometryIsNull);
+
+ try
+ {
+ foreach (IGeometry geometry in geometries)
+ {
+ if (geometry == null)
+ continue;
+
+ if (!Format.Supports(geometry))
+ throw new ArgumentException(MessageGeometriesAreNotSupported, "geometries");
+
+ ApplyWriteGeometry(geometry);
+ }
+ }
+ catch (Exception ex)
+ {
+ throw new IOException(MessageContentWriteError, ex);
+ }
+ }
+
+ ///
+ /// Closes the writer and the underlying stream, and releases any system resources associated with the writer.
+ ///
+ public virtual void Close()
+ {
+ Dispose();
+ }
+
+ #endregion
+
+ #region IDisposable methods
+
+ ///
+ /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.
+ ///
+ public void Dispose()
+ {
+ if (_disposed)
+ return;
+
+ Dispose(true);
+ GC.SuppressFinalize(this);
+ }
+
+ #endregion
+
+ #region Protected methods
+
+ ///
+ /// Apply the write operation for the specified geometry.
+ ///
+ /// The geometry.
+ protected abstract void ApplyWriteGeometry(IGeometry geometry);
+
+ ///
+ /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.
+ ///
+ /// A value indicating whether disposing is performed on the object.
+ protected virtual void Dispose(Boolean disposing)
+ {
+ _disposed = true;
+
+ if (disposing)
+ {
+ switch (_bufferingMode)
+ {
+ case BufferingMode.Minimal:
+ case BufferingMode.Maximal:
+ if (_baseStream != null)
+ _baseStream.Dispose();
+ break;
+ }
+
+ if (_disposeSourceStream && _sourceStream != null)
+ _sourceStream.Dispose();
+ }
+ }
+
+ ///
+ /// Resolves the specified parameter.
+ ///
+ /// The parameter.
+ /// The specified parameter value or the default value if none specified.
+ protected Object ResolveParameter(GeometryStreamParameter parameter)
+ {
+ if (_parameters != null && _parameters.ContainsKey(parameter))
+ return _parameters[parameter];
+
+ return parameter.DefaultValue;
+ }
+
+ ///
+ /// Resolves the specified parameter.
+ ///
+ /// The type of the parameter.
+ /// The parameter.
+ /// The specified parameter value or the default value if none specified.
+ protected T ResolveParameter(GeometryStreamParameter parameter)
+ {
+ if (_parameters != null && _parameters.ContainsKey(parameter) && _parameters[parameter] is T)
+ return (T)_parameters[parameter];
+
+ return (T)parameter.DefaultValue;
+ }
+
+ ///
+ /// Determines whether the specified parameter is provided.
+ ///
+ /// The parameter.
+ /// true if the parameter is provided; otherwise, false .
+ protected Boolean IsProvidedParameter(GeometryStreamParameter parameter)
+ {
+ return _parameters != null && _parameters.ContainsKey(parameter);
+ }
+
+ ///
+ /// Opens the file on the specified path.
+ ///
+ /// The path of the file.
+ /// The stream of the file.
+ protected Stream OpenPath(String path)
+ {
+ return OpenPath(ResolvePath(path));
+ }
+
+ ///
+ /// Opens the file on the specified path.
+ ///
+ /// The path of the file.
+ /// The stream of the file.
+ protected Stream OpenPath(Uri path)
+ {
+ IFileSystem fileSystem = FileSystemContainer.GetFileSystemForScheme(path.Scheme);
+ Stream stream;
+
+ if (path.IsAbsoluteUri)
+ stream = fileSystem.OpenFile(path.AbsolutePath, FileMode.OpenOrCreate, FileAccess.Write);
+ else
+ stream = fileSystem.OpenFile(path.OriginalString, FileMode.OpenOrCreate, FileAccess.Write);
+
+ // apply buffering
+ switch (_bufferingMode)
+ {
+ case BufferingMode.Minimal:
+ return new ProxyStream(stream, ProxyStreamOptions.ForceProxy | ProxyStreamOptions.SingleAccess);
+ case BufferingMode.Maximal:
+ return new MemoryBufferedStream(stream, true);
+ default:
+ return stream;
+ }
+ }
+
+ #endregion
+
+ #region Private methods
+
+ ///
+ /// Resolves the path.
+ ///
+ /// The path string.
+ /// The path URI.
+ /// The path is null.
+ ///
+ /// The path is empty, or consists only of whitespace characters.
+ /// or
+ /// The path is in an invalid format.
+ ///
+ private static Uri ResolvePath(String path)
+ {
+ Uri pathUri;
+
+ if (path == null)
+ throw new ArgumentNullException("path", MessagePathIsNull);
+ if (String.IsNullOrEmpty(path))
+ throw new ArgumentException(MessagePathIsEmpty, "path");
+ if (!Uri.TryCreate(path, UriKind.RelativeOrAbsolute, out pathUri))
+ throw new ArgumentException(MessagePathIsInInvalidFormat, "path");
+
+ return pathUri;
+ }
+
+ ///
+ /// Resolves the stream.
+ ///
+ /// The path.
+ /// The stream.
+ /// Exception occurred during stream opening.
+ private static Stream ResolveStream(Uri path)
+ {
+ try
+ {
+ IFileSystem fileSystem = FileSystemContainer.GetFileSystemForScheme(path.Scheme);
+ if (path.IsAbsoluteUri)
+ return fileSystem.OpenFile(path.AbsolutePath, FileMode.Create, FileAccess.Write);
+ else
+ return fileSystem.OpenFile(path.OriginalString, FileMode.Create, FileAccess.Write);
+ }
+ catch (Exception ex)
+ {
+ throw new IOException(MessageContentOpenError, ex);
+ }
+ }
+
+ #endregion
+ }
+}
diff --git a/src/IO/IO.csproj b/src/IO/IO.csproj
new file mode 100644
index 0000000..402e8df
--- /dev/null
+++ b/src/IO/IO.csproj
@@ -0,0 +1,37 @@
+
+
+
+ netstandard2.0
+
+
+
+ netstandard2.1
+ AEGIS.IO
+ AEGIS.IO
+ Roberto Giachetta et al.
+ Eötvös Loránd University (ELTE)
+ Input/output library of the AEGIS framework.
+ Copyright Roberto Giachetta 2016-2021
+ http://opensource.org/licenses/ECL-2.0
+ https://github.com/robertogiachetta/aegis
+ 0.1.0
+ True
+
+ ..\analyzer.ruleset
+
+
+
+ full
+ True
+ ..\..\docs\AEGIS.IO.xml
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/IO/Utilities/MemoryBufferedStream.cs b/src/IO/Utilities/MemoryBufferedStream.cs
new file mode 100644
index 0000000..4b60610
--- /dev/null
+++ b/src/IO/Utilities/MemoryBufferedStream.cs
@@ -0,0 +1,218 @@
+//
+// Copyright 2016-2021 Roberto Giachetta. Licensed under the
+// Educational Community License, Version 2.0 (the "License"); you may
+// not use this file except in compliance with the License. You may
+// obtain a copy of the License at
+// http://opensource.org/licenses/ECL-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an "AS IS"
+// BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
+// or implied. See the License for the specific language governing
+// permissions and limitations under the License.
+//
+
+using System;
+using System.IO;
+
+namespace AEGIS.IO.Utilities
+{
+ ///
+ /// Represents a memory buffered stream.
+ ///
+ public class MemoryBufferedStream : Stream
+ {
+ #region Private fields
+
+ ///
+ /// The source stream.
+ ///
+ private Stream _sourceStream;
+
+ ///
+ /// The memory stream.
+ ///
+ private MemoryStream _memoryStream;
+
+ ///
+ /// A value indicating whether to dispose the source stream.
+ ///
+ private Boolean _disposeSourceStream;
+
+ #endregion
+
+ #region Constructors
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// The source stream.
+ /// A value indicating whether to dispose the source stream.
+ public MemoryBufferedStream(Stream sourceStream, Boolean disposeSourceStream = false)
+ {
+ _sourceStream = sourceStream;
+ _memoryStream = new MemoryStream();
+ _disposeSourceStream = disposeSourceStream;
+
+ if (_sourceStream.CanRead)
+ {
+ Byte[] inputBytes = new Byte[1 << 20];
+ Int32 bytesRead = 0;
+
+ while ((bytesRead = _sourceStream.Read(inputBytes, 0, inputBytes.Length)) > 0)
+ {
+ _memoryStream.Write(inputBytes, 0, bytesRead);
+ }
+ _memoryStream.Flush();
+ _memoryStream.Seek(0, SeekOrigin.Begin);
+ }
+ }
+
+ #endregion
+
+ #region Stream properties
+
+ ///
+ /// Gets a value indicating whether the current stream supports reading.
+ ///
+ /// true if the stream supports reading; otherwise, false.
+ public override Boolean CanRead
+ {
+ get { return true; }
+ }
+
+ ///
+ /// Gets a value indicating whether the current stream supports seeking.
+ ///
+ /// true if the stream supports seeking; otherwise, false.
+ public override Boolean CanSeek
+ {
+ get { return true; }
+ }
+
+ ///
+ /// Gets a value indicating whether the current stream supports writing.
+ ///
+ /// true if the stream supports writing; otherwise, false.
+ public override Boolean CanWrite
+ {
+ get { return true; }
+ }
+
+ ///
+ /// Gets the length in bytes of the stream.
+ ///
+ /// A long value representing the length of the stream in bytes.
+ public override Int64 Length
+ {
+ get { return _memoryStream.Length; }
+ }
+
+ ///
+ /// Gets or sets the position within the current stream.
+ ///
+ /// The current position within the stream.
+ public override Int64 Position
+ {
+ get
+ {
+ return _memoryStream.Position;
+ }
+ set
+ {
+ _memoryStream.Position = value;
+ }
+ }
+
+ #endregion
+
+ #region Stream methods
+
+ ///
+ /// Clears all buffers for this stream and causes any buffered data to be written to the underlying device.
+ ///
+ public override void Flush()
+ {
+ _memoryStream.Flush();
+ }
+
+ ///
+ /// Reads a sequence of bytes from the current stream and advances the position within the stream by the number of bytes read.
+ ///
+ /// An array of bytes. When this method returns, the buffer contains the specified byte array with the values between and ( + - 1) replaced by the bytes read from the current source.
+ /// The zero-based byte offset in at which to begin storing the data read from the current stream.
+ /// The maximum number of bytes to be read from the current stream.
+ /// The total number of bytes read into the buffer. This can be less than the number of bytes requested if that many bytes are not currently available, or zero (0) if the end of the stream has been reached.
+ public override Int32 Read(Byte[] buffer, Int32 offset, Int32 count)
+ {
+ return _memoryStream.Read(buffer, offset, count);
+ }
+
+ ///
+ /// Sets the position within the current stream.
+ ///
+ /// A byte offset relative to the parameter.
+ /// A value of type indicating the reference point used to obtain the new position.
+ /// The new position within the current stream.
+ public override Int64 Seek(Int64 offset, SeekOrigin origin)
+ {
+ return _memoryStream.Seek(offset, origin);
+ }
+
+ ///
+ /// Sets the length of the current stream.
+ ///
+ /// The desired length of the current stream in bytes.
+ public override void SetLength(Int64 value)
+ {
+ _memoryStream.SetLength(value);
+ }
+
+ ///
+ /// Writes a sequence of bytes to the current stream and advances the current position within this stream by the number of bytes written.
+ ///
+ /// An array of bytes. This method copies bytes from to the current stream.
+ /// The zero-based byte offset in at which to begin copying bytes to the current stream.
+ /// The number of bytes to be written to the current stream.
+ public override void Write(Byte[] buffer, Int32 offset, Int32 count)
+ {
+ _memoryStream.Write(buffer, offset, count);
+ }
+
+ #endregion
+
+ #region Protected Stream methods
+
+ ///
+ /// Releases the unmanaged resources used by the and optionally releases the managed resources.
+ ///
+ /// true to release both managed and unmanaged resources; false to release only unmanaged resources.
+ protected override void Dispose(Boolean disposing)
+ {
+ if (disposing)
+ {
+ if (_sourceStream.CanWrite)
+ {
+ _memoryStream.Seek(0, SeekOrigin.Begin);
+ Byte[] inputBytes = new Byte[1 << 20];
+ Int32 bytesRead = 0;
+
+ while ((bytesRead = _memoryStream.Read(inputBytes, 0, inputBytes.Length)) > 0)
+ {
+ _sourceStream.Write(inputBytes, 0, bytesRead);
+ }
+ _sourceStream.Flush();
+ }
+
+ _memoryStream.Dispose();
+
+ if (_disposeSourceStream)
+ _sourceStream.Dispose();
+ }
+
+ base.Dispose(disposing);
+ }
+
+ #endregion
+ }
+}
diff --git a/src/IO/Utilities/ProxyStream.cs b/src/IO/Utilities/ProxyStream.cs
new file mode 100644
index 0000000..0f69611
--- /dev/null
+++ b/src/IO/Utilities/ProxyStream.cs
@@ -0,0 +1,800 @@
+//
+// Copyright 2016-2021 Roberto Giachetta. Licensed under the
+// Educational Community License, Version 2.0 (the "License"); you may
+// not use this file except in compliance with the License. You may
+// obtain a copy of the License at
+// http://opensource.org/licenses/ECL-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an "AS IS"
+// BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
+// or implied. See the License for the specific language governing
+// permissions and limitations under the License.
+//
+
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+
+namespace AEGIS.IO.Utilities
+{
+ ///
+ /// Works as a proxy for an underlying stream.
+ ///
+ public class ProxyStream : Stream
+ {
+ #region Constant fields
+
+ ///
+ /// The size of the buffer arrays used to cache data. This field is constant.
+ ///
+ private const Int32 DefaultBufferSize = 1 << 16; // (64 KB)
+
+ private readonly Byte[] EmptyBuffer;
+
+ #endregion
+
+ #region Private types
+
+ ///
+ /// Defines the access types of the stream.
+ ///
+ [Flags]
+ private enum StreamAccessType
+ {
+ ///
+ /// Indicates that the access type is not defined.
+ ///
+ Undefined = 0,
+
+ ///
+ /// Indicates that the stream is readable.
+ ///
+ Readable = 1,
+
+ ///
+ /// Indicates that the stream is writable.
+ ///
+ Writable = 2
+ };
+
+ ///
+ /// Defines the modes for updating data flags.
+ ///
+ private enum UpdateMode
+ {
+ ///
+ /// Indicates that the flags should be added.
+ ///
+ Add,
+
+ ///
+ /// Indicates that the flags should be removed.
+ ///
+ Remove
+ }
+
+ #endregion
+
+ #region Private fields
+
+ ///
+ /// The underlying stream.
+ ///
+ private Stream _baseStream;
+
+ ///
+ /// Defines whether the instances has been disposed or not.
+ ///
+ private Boolean _disposed;
+
+ ///
+ /// A value indicating whether the bytes can be read or written multiple times or not.
+ ///
+ private Boolean _isSingleAccess;
+
+ ///
+ /// A value indicating whether proxy mode is forced.
+ ///
+ private Boolean _isProxyModeForced;
+
+ ///
+ /// The current position in the stream.
+ ///
+ private Int64 _position;
+
+ ///
+ /// The length of the stream.
+ ///
+ private Int64 _length;
+
+ ///
+ /// The access type of the stream.
+ ///
+ private StreamAccessType _accessType;
+
+ ///
+ /// The bit flag arrays.
+ ///
+ private Dictionary _bitFlags;
+
+ ///
+ /// The data containing arrays.
+ ///
+ private Dictionary _buffers;
+
+ ///
+ /// A value indicating whether to dispose the underlying stream.
+ ///
+ private Boolean _disposeBaseStream;
+
+ ///
+ /// The number of bytes stored in the buffers.
+ ///
+ private Int32 _bufferSize;
+
+ #endregion
+
+ #region Constructor
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// The underlying stream.
+ /// The stream is null.
+ /// The stream does not support reading and writing.
+ public ProxyStream(Stream stream)
+ : this(stream, false, ProxyStreamOptions.Default, DefaultBufferSize)
+ {
+ }
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// The underlying stream.
+ /// The proxy stream options.
+ /// The stream is null.
+ /// The stream does not support reading and writing.
+ public ProxyStream(Stream stream, ProxyStreamOptions options)
+ : this(stream, false, options, DefaultBufferSize)
+ {
+ }
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// The underlying stream.
+ /// A value indicating whether to dispose the underlying stream.
+ /// The proxy stream options.
+ /// The stream is null.
+ /// The stream does not support reading and writing.
+ public ProxyStream(Stream stream, Boolean disposeStream, ProxyStreamOptions options)
+ : this(stream, disposeStream, options, DefaultBufferSize)
+ {
+
+ }
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// The underlying stream.
+ /// A value indicating whether to dispose the underlying stream.
+ /// The proxy stream options.
+ /// Size of the buffer.
+ /// The buffer size is less than 1.
+ /// The stream is null.
+ /// The stream does not support reading and writing.
+ public ProxyStream(Stream underlyingStream, Boolean disposeUnderlyingStream, ProxyStreamOptions options, Int32 bufferSize)
+ {
+ if (underlyingStream == null)
+ throw new ArgumentNullException("stream", "The stream is null.");
+
+ if (!underlyingStream.CanRead && !underlyingStream.CanWrite)
+ throw new NotSupportedException("The stream does not support reading and writing.");
+
+ if (bufferSize < 1)
+ throw new ArgumentOutOfRangeException("bufferSize", "The buffer size is less than 1.");
+
+ _baseStream = underlyingStream;
+ _isSingleAccess = options.HasFlag(ProxyStreamOptions.SingleAccess);
+ _isProxyModeForced = options.HasFlag(ProxyStreamOptions.ForceProxy);
+ _bufferSize = bufferSize;
+
+ if (_baseStream.CanWrite && !_baseStream.CanRead)
+ InitializeStream(StreamAccessType.Writable);
+ else if (!_baseStream.CanWrite && _baseStream.CanRead)
+ InitializeStream(StreamAccessType.Readable);
+
+ _disposed = false;
+ _disposeBaseStream = disposeUnderlyingStream;
+
+ EmptyBuffer = new Byte[_bufferSize];
+ }
+
+ #endregion
+
+ #region Stream properties
+
+ ///
+ /// Gets or sets the position within the current stream.
+ ///
+ public override Int64 Position
+ {
+ get
+ {
+ return _position;
+ }
+ set
+ {
+ if (_position != value)
+ {
+ Seek(_position, SeekOrigin.Begin);
+ }
+ }
+ }
+
+ ///
+ /// Gets the length in bytes of the stream.
+ ///
+ public override Int64 Length { get { return _length; } }
+
+ ///
+ /// Gets a value indicating whether the current stream supports seeking.
+ ///
+ public override Boolean CanSeek
+ {
+ get { return true; }
+ }
+
+ ///
+ /// Gets a value indicating whether the current stream supports writing.
+ ///
+ public override Boolean CanWrite
+ {
+ get { return (_accessType == StreamAccessType.Writable || _accessType == StreamAccessType.Undefined) ? true : false; }
+ }
+
+ ///
+ /// Gets a value indicating whether the current stream supports reading.
+ ///
+ public override Boolean CanRead
+ {
+ get { return (_accessType == StreamAccessType.Readable || _accessType == StreamAccessType.Undefined) ? true : false; }
+ }
+
+ #endregion
+
+ #region Stream methods
+
+ ///
+ /// When overridden in a derived class, sets the position within the current stream.
+ ///
+ /// A byte offset relative to the parameter.
+ /// A value of type indicating the reference point used to obtain the new position.
+ ///
+ /// The new position within the current stream.
+ ///
+ /// Method was called after the stream was closed.
+ ///
+ public override Int64 Seek(Int64 offset, SeekOrigin origin)
+ {
+ if (_disposed)
+ throw new ObjectDisposedException("Method was called after the stream was closed.");
+
+ if (!_isProxyModeForced && _baseStream.CanSeek)
+ return _baseStream.Seek(offset, origin);
+
+ switch (origin)
+ {
+ case SeekOrigin.Begin:
+ _position = offset;
+ break;
+ case SeekOrigin.Current:
+ _position += offset;
+ break;
+ case SeekOrigin.End:
+ _position = offset + _baseStream.Length;
+ break;
+ }
+
+ return _position;
+ }
+
+ ///
+ /// Reads a sequence of bytes from the current stream, stores them in the cache and advances the position within the stream by the number of bytes read.
+ ///
+ /// An array of bytes. When this method returns, the buffer contains the specified byte array with the values between and ( + - 1) replaced by the bytes read from the current source.
+ /// The zero-based byte offset in at which to begin storing the data read from the current stream.
+ /// The maximum number of bytes to be read from the current stream.
+ /// The total number of bytes read into the buffer.
+ /// Method was called after the stream was closed.
+ /// The buffer is null.
+ /// The sum of offset and count is larger than the buffer length.
+ /// The offset or count is negative.
+ /// The stream does not support reading.
+ public override Int32 Read(Byte[] buffer, Int32 offset, Int32 count)
+ {
+ if (_disposed)
+ throw new ObjectDisposedException("Method was called after the stream was closed.");
+
+ if (buffer == null)
+ throw new ArgumentNullException("The buffer is null.");
+
+ if (offset + count > buffer.Length)
+ throw new ArgumentException("The sum of offset and count is larger than the buffer length.");
+
+ if (offset < 0 || count < 0)
+ throw new ArgumentOutOfRangeException("The offset or count is negative.");
+
+ // check whether data can be directly read
+ if (!_isProxyModeForced && _baseStream.CanSeek && _baseStream.CanRead)
+ {
+ // read the data from the source stream
+ return _baseStream.Read(buffer, offset, count);
+ }
+
+ InitializeStream(StreamAccessType.Readable);
+
+ if (_accessType != StreamAccessType.Readable)
+ throw new NotSupportedException("The stream does not support reading.");
+
+ if (_position >= _length)
+ return 0;
+
+ if (!IsBufferAvailable(_position, Math.Min(count, _baseStream.Position - _position)))
+ throw new InvalidOperationException("The specified data was already read.");
+
+ // check whether data needs to be read from the underlying stream
+ if (_position + count > _baseStream.Position)
+ {
+ try
+ {
+ ReadDataFromStream(_position + count - _baseStream.Position);
+ }
+ catch (Exception ex)
+ {
+ throw new IOException("Error occurred during underlying stream reading.", ex);
+ }
+ }
+
+ return ReadDataFromBuffer(buffer, offset, count);
+ }
+
+ ///
+ /// Writes a sequence of bytes to the current stream and advances the current position within this stream by the number of bytes written.
+ ///
+ /// An array of bytes. This method copies bytes from to the current stream.
+ /// The zero-based byte offset in at which to begin copying bytes to the current stream.
+ /// The number of bytes to be written to the current stream.
+ /// Method was called after the stream was closed.
+ /// The buffer is null
+ /// The sum of offset and count is larger than the buffer length.
+ /// The offset or count is negative.
+ /// The stream does not support writing.
+ public override void Write(Byte[] buffer, Int32 offset, Int32 count)
+ {
+ if (_disposed)
+ throw new ObjectDisposedException("Method was called after the stream was closed.");
+
+ if (!_isProxyModeForced && _baseStream.CanSeek)
+ {
+ _baseStream.Write(buffer, offset, count);
+ return;
+ }
+
+ if (buffer == null)
+ throw new ArgumentNullException("The buffer is null");
+
+ if (offset + count > buffer.Length)
+ throw new ArgumentException("The sum of offset and count is larger than the buffer length.");
+
+ if (offset < 0 || count < 0)
+ throw new ArgumentOutOfRangeException("The offset or count is negative.");
+
+ InitializeStream(StreamAccessType.Writable);
+
+ if (_accessType != StreamAccessType.Writable)
+ throw new NotSupportedException("The stream does not support writing.");
+
+ if (!IsBufferAvailable(_position, Math.Min(count, _baseStream.Position - _position)))
+ throw new InvalidOperationException("Data was already written at the specified position.");
+
+ WriteDataIntoBuffer(buffer, offset, count);
+
+ WriteDataIntoStream(false);
+ }
+
+ ///
+ /// Sets the length of the current stream.
+ ///
+ /// The desired length of the current stream in bytes.
+ /// Method was called after the stream was closed.
+ /// The stream is not writable or does not support seeking.
+ public override void SetLength(Int64 value)
+ {
+ if (_disposed)
+ throw new ObjectDisposedException("Method was called after the stream was closed.");
+
+ if (_accessType == StreamAccessType.Readable)
+ throw new InvalidOperationException("The stream is not writable.");
+
+ if (_accessType == StreamAccessType.Undefined)
+ _accessType = StreamAccessType.Writable;
+
+ if (_length < value)
+ {
+ CreateBuffer(_length, value - _length);
+ }
+
+ if (_length > value)
+ {
+ RemoveBuffer(value, _length - value);
+ }
+
+ _length = value;
+ }
+
+ ///
+ /// Clears all buffers for this stream and causes any buffered data to be written to the underlying stream.
+ ///
+ public override void Flush()
+ {
+ if (_accessType != StreamAccessType.Writable)
+ return;
+
+ WriteDataIntoStream(true);
+ }
+
+ #endregion
+
+ #region Private methods
+
+ ///
+ /// Gets a value indicating whether the buffers are available.
+ ///
+ /// The offset of the first byte with respect to the start of the stream.
+ /// The number of bytes.
+ /// true if the specified data is already disposed; otherwise, false .
+ private Boolean IsBufferAvailable(Int64 offset, Int64 count)
+ {
+ Int32 bufferIndex = (Int32)(offset / _bufferSize);
+ while (count > 0)
+ {
+ if (!_buffers.ContainsKey(bufferIndex))
+ return false;
+
+ count -= _bufferSize;
+ bufferIndex++;
+ }
+
+ return true;
+ }
+
+ private Boolean IsBufferFilled(Int32 bufferIndex)
+ {
+ if (!_isSingleAccess)
+ return false;
+
+ return _bitFlags != null && _bitFlags.ContainsKey(bufferIndex) && _bitFlags[bufferIndex].All(value => value == Byte.MaxValue);
+ }
+
+ ///
+ /// Reads the necessary bytes from the underlying stream and stores them in the buffers.
+ ///
+ /// The number of bytes to read.
+ private void ReadDataFromStream(Int64 count)
+ {
+ Int32 bufferIndex = (Int32)(_baseStream.Position / _bufferSize);
+
+ while (count > 0)
+ {
+ // read content
+ Byte[] bytes = new Byte[_bufferSize];
+
+ Int32 numberOfBytesRead = _baseStream.Read(bytes, 0, bytes.Length);
+
+ _buffers.Add(bufferIndex, bytes);
+ UpdateFlags(bufferIndex, 0, Math.Min(numberOfBytesRead, bytes.Length), UpdateMode.Add);
+
+ if (numberOfBytesRead < bytes.Length)
+ return;
+
+ count -= numberOfBytesRead;
+
+ bufferIndex++;
+ }
+ }
+
+ ///
+ /// Reads the data from the buffers.
+ ///
+ /// An array of bytes. When this method returns, the buffer contains the specified byte array with the values between and ( + - 1) replaced by the bytes read from the current source.
+ /// The zero-based byte offset in at which to begin storing the data read from the current stream.
+ /// The maximum number of bytes to be read from the current stream.
+ /// The total number of bytes read from the buffers.
+ private Int32 ReadDataFromBuffer(Byte[] buffer, Int32 offset, Int32 count)
+ {
+ Int32 bufferIndex = (Int32)(_position / _bufferSize);
+
+ Int32 numberOfBytesRead = 0;
+ Int32 readOffset = (Int32)(_position % _bufferSize);
+ Int32 readCount = Math.Min(count, _bufferSize - readOffset);
+
+ while (numberOfBytesRead < count)
+ {
+ if (!_buffers.ContainsKey(bufferIndex))
+ return numberOfBytesRead;
+
+ Array.Copy(_buffers[bufferIndex], readOffset, buffer, offset, readCount);
+
+ UpdateFlags(bufferIndex, readOffset, readCount, UpdateMode.Remove);
+ RemoveBuffer(bufferIndex);
+
+ numberOfBytesRead += readCount;
+ offset += readCount;
+ readOffset = 0;
+ readCount = Math.Min(count - numberOfBytesRead, _bufferSize);
+
+ bufferIndex++;
+ }
+
+ _position += numberOfBytesRead;
+ return numberOfBytesRead;
+ }
+
+ ///
+ /// Writes the data into the buffers.
+ ///
+ /// An array of bytes. This method copies bytes from to the current stream.
+ /// The zero-based byte offset in at which to begin copying bytes to the current stream.
+ /// The number of bytes to be written to the current stream.
+ private void WriteDataIntoBuffer(Byte[] buffer, Int32 offset, Int32 count)
+ {
+ Int32 bufferIndex = (Int32)(_position / _bufferSize);
+
+ Int64 numberOfBytesWritten = 0;
+ Int32 writeOffset = (Int32)(_position % _bufferSize);
+ Int32 writeCount = Math.Min(count, _bufferSize - writeOffset);
+
+ while (numberOfBytesWritten < count)
+ {
+ CreateBuffer(bufferIndex);
+
+ Array.Copy(buffer, offset, _buffers[bufferIndex], writeOffset, writeCount);
+
+ UpdateFlags(bufferIndex, writeOffset, writeCount, UpdateMode.Add);
+
+ numberOfBytesWritten += writeCount;
+ offset += writeCount;
+ writeOffset = 0;
+ writeCount = (Int32)Math.Min(count - numberOfBytesWritten, _bufferSize);
+
+ bufferIndex++;
+ }
+
+ _position += numberOfBytesWritten;
+
+ if (_length < _position)
+ _length = _position;
+ }
+
+ ///
+ /// Writes the data into the stream.
+ ///
+ /// A value indicating whether the writing is forced.
+ private void WriteDataIntoStream(Boolean forced)
+ {
+ if (!_isSingleAccess && !forced)
+ return;
+
+ Int32 bufferIndex = (Int32)(_baseStream.Position / _bufferSize);
+
+ Int64 numberOfBytesToWrite = _length - _baseStream.Position;
+ Int32 writeOffset = (Int32)(_baseStream.Position % _bufferSize);
+ Int32 writeCount = (Int32)Math.Min(numberOfBytesToWrite, _bufferSize - writeOffset);
+
+ while (numberOfBytesToWrite > 0)
+ {
+ if (!_buffers.ContainsKey(bufferIndex))
+ {
+ if (!forced)
+ break;
+
+ _baseStream.Write(EmptyBuffer, writeOffset, writeCount);
+ }
+ else
+ {
+ if (!forced && !IsBufferFilled(bufferIndex))
+ break;
+
+ _baseStream.Write(_buffers[bufferIndex], writeOffset, writeCount);
+
+ UpdateFlags(bufferIndex, writeOffset, writeCount, UpdateMode.Remove);
+ RemoveBuffer(bufferIndex);
+ }
+
+ numberOfBytesToWrite -= writeCount;
+ writeOffset = 0;
+ writeCount = (Int32)Math.Min(numberOfBytesToWrite, _bufferSize);
+
+ bufferIndex++;
+ }
+ }
+
+ ///
+ /// Updates the data flags with the specified availability.
+ ///
+ /// The index of the buffer in which the update is performed.
+ /// The offset of the first byte within the specified buffer.
+ /// The number of bytes within the buffer.
+ /// The flag update mode.
+ private void UpdateFlags(Int32 bufferIndex, Int32 offset, Int32 count, UpdateMode mode)
+ {
+ if (!_isSingleAccess)
+ return;
+
+ if (!_bitFlags.ContainsKey(bufferIndex))
+ {
+ if (mode == UpdateMode.Add)
+ {
+ _bitFlags.Add(bufferIndex, new Byte[_bufferSize / 8]);
+ }
+ else
+ {
+ return;
+ }
+ }
+
+ Int32 byteIndex = offset / 8;
+
+ Int32 numberOfBitsSet = 0;
+ Int32 bitOffset = offset % 8;
+ Int32 bitCount = Math.Min(count, 8 - bitOffset);
+
+ while (numberOfBitsSet < count)
+ {
+ switch (mode)
+ {
+ case UpdateMode.Add:
+ _bitFlags[bufferIndex][byteIndex] |= GetBitMask(bitOffset, bitCount);
+ break;
+ case UpdateMode.Remove:
+ _bitFlags[bufferIndex][byteIndex] ^= GetBitMask(bitOffset, bitCount);
+ break;
+ }
+
+ byteIndex++;
+ numberOfBitsSet += bitCount;
+
+ bitOffset = 0;
+ bitCount = Math.Min(count - numberOfBitsSet, 8);
+ }
+ }
+
+ ///
+ /// Returns the bit mask for the specified offset and count.
+ ///
+ /// The offset of the first byte.
+ /// The number of bytes.
+ /// The bit mask used for updating the data flags.
+ private Byte GetBitMask(Int32 offset, Int32 count)
+ {
+ return (Byte)(256 - (1 << (8 - count)) >> offset);
+ }
+
+ ///
+ /// Creates buffer arrays.
+ ///
+ /// The offset of the first byte.
+ /// The number of bytes.
+ private void CreateBuffer(Int64 offset, Int64 count)
+ {
+ Int32 startBufferIndex = (Int32)(offset / _bufferSize);
+ Int32 endBufferIndex = (Int32)((offset + count) / _bufferSize);
+
+ for (Int32 bufferIndex = startBufferIndex; bufferIndex <= endBufferIndex; bufferIndex++)
+ {
+ CreateBuffer(bufferIndex);
+ }
+ }
+
+ ///
+ /// Creates a buffer array.
+ ///
+ /// The index of the buffer.
+ private void CreateBuffer(Int32 bufferIndex)
+ {
+ if (_buffers.ContainsKey(bufferIndex))
+ return;
+
+ _buffers.Add(bufferIndex, new Byte[_bufferSize]);
+
+ if (_isSingleAccess)
+ _bitFlags.Add(bufferIndex, new Byte[_bufferSize / 8]);
+ }
+
+ ///
+ /// Removes buffer arrays.
+ ///
+ /// The offset of the first byte.
+ /// The number of bytes.
+ private void RemoveBuffer(Int64 offset, Int64 count)
+ {
+ Int32 startBufferIndex = (Int32)(offset / _bufferSize) + 1;
+ Int32 endBufferIndex = (Int32)((offset + count) / _bufferSize);
+
+ for (Int32 bufferIndex = startBufferIndex; bufferIndex <= endBufferIndex; bufferIndex++)
+ RemoveBuffer(bufferIndex);
+ }
+
+ ///
+ /// Removes a buffer array.
+ ///
+ /// The index of the buffer.
+ private void RemoveBuffer(Int32 bufferIndex)
+ {
+ if (!_isSingleAccess || !_buffers.ContainsKey(bufferIndex))
+ return;
+
+ if (_bitFlags[bufferIndex].All(value => value == 0))
+ {
+ _buffers.Remove(bufferIndex);
+ _bitFlags.Remove(bufferIndex);
+ }
+ }
+
+ ///
+ /// Initializes the stream.
+ ///
+ /// The access type.
+ private void InitializeStream(StreamAccessType type)
+ {
+ if (_accessType != StreamAccessType.Undefined)
+ return;
+
+ switch (type)
+ {
+ case StreamAccessType.Readable:
+ _accessType = StreamAccessType.Readable;
+ _length = _baseStream.Length;
+ break;
+ case StreamAccessType.Writable:
+ _accessType = StreamAccessType.Writable;
+ _length = 0;
+ break;
+ }
+
+ _position = 0;
+ _buffers = new Dictionary();
+
+ if (_isSingleAccess)
+ _bitFlags = new Dictionary();
+ }
+
+ #endregion
+
+ #region Protected methods
+
+ ///
+ /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.
+ ///
+ /// A value indicating whether disposing is performed on the object.
+ protected override void Dispose(Boolean disposing)
+ {
+ _disposed = true;
+
+ if (disposing)
+ {
+ if (_accessType == StreamAccessType.Writable)
+ WriteDataIntoStream(true);
+
+ if (_disposeBaseStream)
+ _baseStream.Dispose();
+ }
+ }
+
+ #endregion
+ }
+}
+
diff --git a/src/IO/Utilities/ProxyStreamOptions.cs b/src/IO/Utilities/ProxyStreamOptions.cs
new file mode 100644
index 0000000..e16eeca
--- /dev/null
+++ b/src/IO/Utilities/ProxyStreamOptions.cs
@@ -0,0 +1,40 @@
+//
+// Copyright 2016-2021 Roberto Giachetta. Licensed under the
+// Educational Community License, Version 2.0 (the "License"); you may
+// not use this file except in compliance with the License. You may
+// obtain a copy of the License at
+// http://opensource.org/licenses/ECL-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an "AS IS"
+// BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
+// or implied. See the License for the specific language governing
+// permissions and limitations under the License.
+//
+
+using System;
+
+namespace AEGIS.IO.Utilities
+{
+ ///
+ /// Defines options of the proxy stream.
+ ///
+ [Flags]
+ public enum ProxyStreamOptions
+ {
+ ///
+ /// Indicates that the default behavior is used.
+ ///
+ Default = 0,
+
+ ///
+ /// Indicates to force proxy usage independently of underlying stream.
+ ///
+ ForceProxy = 1,
+
+ ///
+ /// Indicates that each byte can only be written read one times.
+ ///
+ SingleAccess = 2
+ }
+}
diff --git a/src/Storage/FileSystemContainer.cs b/src/Storage/FileSystemContainer.cs
new file mode 100644
index 0000000..a7c586b
--- /dev/null
+++ b/src/Storage/FileSystemContainer.cs
@@ -0,0 +1,149 @@
+//
+// Copyright 2016-2021 Roberto Giachetta. Licensed under the
+// Educational Community License, Version 2.0 (the "License"); you may
+// not use this file except in compliance with the License. You may
+// obtain a copy of the License at
+// http://opensource.org/licenses/ECL-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an "AS IS"
+// BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
+// or implied. See the License for the specific language governing
+// permissions and limitations under the License.
+//
+
+namespace AEGIS.Storage
+{
+ using System;
+ using System.Collections.Generic;
+ using System.Linq;
+
+ ///
+ /// Represents a static factory class for subtypes.
+ ///
+ /// Máté Cserép
+ public static class FileSystemContainer
+ {
+ ///
+ /// Registry of scheme and the corresponding subtypes.
+ ///
+ private static readonly Dictionary Registry = new Dictionary();
+
+ ///
+ /// Initializes static members of the class.
+ ///
+ static FileSystemContainer()
+ {
+ RegisterAllSchemes();
+ }
+
+ ///
+ /// Gets a value indicating whether registry is initialized.
+ ///
+ ///
+ /// The registry is considered initialized if it is not empty.
+ ///
+ public static bool IsInitialized => Registry.Count > 0;
+
+ ///
+ /// Registers a new file system scheme.
+ ///
+ /// The scheme of the file system.
+ /// Type of the file system.
+ /// The scheme cannot be null.
+ /// The file system type cannot be null.
+ /// Only subtypes of the IFileSystem interface can be registered.
+ public static void RegisterScheme(string scheme, Type fileSystemType)
+ {
+ if(scheme == null)
+ throw new ArgumentNullException(nameof(scheme), "The scheme is null.");
+ if(fileSystemType == null)
+ throw new ArgumentNullException(nameof(fileSystemType), "The file system type is null.");
+ if (!fileSystemType.IsSubclassOf(typeof(IFileSystem)))
+ throw new ArgumentException("Only subtypes of the IFileSystem interface can be registered.", nameof(fileSystemType));
+
+ Registry.Add(scheme, fileSystemType);
+ }
+
+ ///
+ /// Unregisters a file system scheme.
+ ///
+ /// The scheme.
+ /// The scheme cannot be null.
+ public static void UnregisterScheme(string scheme)
+ {
+ if (scheme == null)
+ throw new ArgumentNullException(nameof(scheme), "The scheme is null.");
+
+ Registry.Remove(scheme);
+ }
+
+ ///
+ /// Instantiates an object for the requested scheme.
+ ///
+ /// The scheme.
+ /// A new file system object for the given scheme.
+ /// The scheme cannot be null.
+ public static IFileSystem GetFileSystemForScheme(string scheme)
+ {
+ if (scheme == null)
+ throw new ArgumentNullException(nameof(scheme), "The scheme is null.");
+
+ return (IFileSystem)Activator.CreateInstance(Registry[scheme]);
+
+ }
+
+ ///
+ /// Instantiates an appropriate object for the requested path.
+ ///
+ /// The path.
+ /// A new file system object for the given path.
+ /// The path is null.
+ public static IFileSystem GetFileSystemForPath(Uri path)
+ {
+ if (path == null)
+ throw new ArgumentNullException(nameof(path), "The path is null.");
+
+ return GetFileSystemForScheme(path.Scheme);
+ }
+
+ ///
+ /// Instantiates an appropriate object for the requested path.
+ ///
+ /// The path.
+ /// A new file system object for the given path.
+ /// The path is null.
+ public static IFileSystem GetFileSystemForPath(string path)
+ {
+ Uri pathUri;
+
+ if (path == null)
+ throw new ArgumentNullException(nameof(path), "The path is null.");
+ if (String.IsNullOrEmpty(path))
+ throw new ArgumentException("The path is empty.", nameof(path));
+ if (!Uri.TryCreate(path, UriKind.RelativeOrAbsolute, out pathUri))
+ throw new ArgumentException("The path is in an invalid format.", nameof(path));
+
+ return GetFileSystemForPath(pathUri);
+ }
+
+ ///
+ /// Registers all schemes available in the loaded assemblies.
+ ///
+ public static void RegisterAllSchemes()
+ {
+ foreach (var assembly in AppDomain.CurrentDomain.GetAssemblies())
+ {
+ var types = assembly.GetTypes()
+ .Where(t => t.IsSubclassOf(typeof(IFileSystem)))
+ .Where(t => !t.IsAbstract);
+
+ foreach (var type in types)
+ {
+ var schemeField = type.GetField("UriScheme");
+ RegisterScheme(schemeField.GetRawConstantValue().ToString(), type);
+ }
+ }
+ }
+ }
+}