1 // Licensed to the .NET Foundation under one or more agreements. 2 // The .NET Foundation licenses this file to you under the MIT license. 3 // See the LICENSE file in the project root for more information. 4 5 using System.Diagnostics; 6 7 namespace System.Runtime.Versioning 8 { 9 [Serializable] 10 public sealed class FrameworkName : IEquatable<FrameworkName> 11 { 12 private readonly string _identifier; 13 private readonly Version _version; 14 private readonly string _profile; 15 private string _fullName; 16 17 private const char ComponentSeparator = ','; 18 private const char KeyValueSeparator = '='; 19 private const char VersionValuePrefix = 'v'; 20 private const string VersionKey = "Version"; 21 private const string ProfileKey = "Profile"; 22 23 private static readonly char[] s_componentSplitSeparator = { ComponentSeparator }; 24 25 public string Identifier 26 { 27 get 28 { 29 Debug.Assert(_identifier != null); 30 return _identifier; 31 } 32 } 33 34 public Version Version 35 { 36 get 37 { 38 Debug.Assert(_version != null); 39 return _version; 40 } 41 } 42 43 public string Profile 44 { 45 get 46 { 47 Debug.Assert(_profile != null); 48 return _profile; 49 } 50 } 51 52 public string FullName 53 { 54 get 55 { 56 if (_fullName == null) 57 { 58 if (string.IsNullOrEmpty(Profile)) 59 { 60 _fullName = 61 Identifier + 62 ComponentSeparator + VersionKey + KeyValueSeparator + VersionValuePrefix + 63 Version.ToString(); 64 } 65 else 66 { 67 _fullName = 68 Identifier + 69 ComponentSeparator + VersionKey + KeyValueSeparator + VersionValuePrefix + 70 Version.ToString() + 71 ComponentSeparator + ProfileKey + KeyValueSeparator + 72 Profile; 73 } 74 } 75 Debug.Assert(_fullName != null); 76 return _fullName; 77 } 78 } 79 Equals(object obj)80 public override bool Equals(object obj) 81 { 82 return Equals(obj as FrameworkName); 83 } 84 Equals(FrameworkName other)85 public bool Equals(FrameworkName other) 86 { 87 if (object.ReferenceEquals(other, null)) 88 { 89 return false; 90 } 91 92 return Identifier == other.Identifier && 93 Version == other.Version && 94 Profile == other.Profile; 95 } 96 GetHashCode()97 public override int GetHashCode() 98 { 99 return Identifier.GetHashCode() ^ Version.GetHashCode() ^ Profile.GetHashCode(); 100 } 101 ToString()102 public override string ToString() 103 { 104 return FullName; 105 } 106 FrameworkName(string identifier, Version version)107 public FrameworkName(string identifier, Version version) 108 : this(identifier, version, null) 109 { 110 } 111 FrameworkName(string identifier, Version version, string profile)112 public FrameworkName(string identifier, Version version, string profile) 113 { 114 if (identifier == null) 115 { 116 throw new ArgumentNullException(nameof(identifier)); 117 } 118 119 identifier = identifier.Trim(); 120 if (identifier.Length == 0) 121 { 122 throw new ArgumentException(SR.Format(SR.net_emptystringcall, nameof(identifier)), nameof(identifier)); 123 } 124 if (version == null) 125 { 126 throw new ArgumentNullException(nameof(version)); 127 } 128 129 _identifier = identifier; 130 _version = version; 131 _profile = (profile == null) ? string.Empty : profile.Trim(); 132 } 133 134 // Parses strings in the following format: "<identifier>, Version=[v|V]<version>, Profile=<profile>" 135 // - The identifier and version is required, profile is optional 136 // - Only three components are allowed. 137 // - The version string must be in the System.Version format; an optional "v" or "V" prefix is allowed FrameworkName(string frameworkName)138 public FrameworkName(string frameworkName) 139 { 140 if (frameworkName == null) 141 { 142 throw new ArgumentNullException(nameof(frameworkName)); 143 } 144 if (frameworkName.Length == 0) 145 { 146 throw new ArgumentException(SR.Format(SR.net_emptystringcall, nameof(frameworkName)), nameof(frameworkName)); 147 } 148 149 string[] components = frameworkName.Split(s_componentSplitSeparator); 150 151 // Identifier and Version are required, Profile is optional. 152 if (components.Length < 2 || components.Length > 3) 153 { 154 throw new ArgumentException(SR.Argument_FrameworkNameTooShort, nameof(frameworkName)); 155 } 156 157 // 158 // 1) Parse the "Identifier", which must come first. Trim any whitespace 159 // 160 _identifier = components[0].Trim(); 161 162 if (_identifier.Length == 0) 163 { 164 throw new ArgumentException(SR.Argument_FrameworkNameInvalid, nameof(frameworkName)); 165 } 166 167 bool versionFound = false; 168 _profile = string.Empty; 169 170 // 171 // The required "Version" and optional "Profile" component can be in any order 172 // 173 for (int i = 1; i < components.Length; i++) 174 { 175 // Get the key/value pair separated by '=' 176 string component = components[i]; 177 int separatorIndex = component.IndexOf(KeyValueSeparator); 178 179 if (separatorIndex == -1 || separatorIndex != component.LastIndexOf(KeyValueSeparator)) 180 { 181 throw new ArgumentException(SR.Argument_FrameworkNameInvalid, nameof(frameworkName)); 182 } 183 184 // Get the key and value, trimming any whitespace 185 string key = component.Substring(0, separatorIndex).Trim(); 186 string value = component.Substring(separatorIndex + 1).Trim(); 187 188 // 189 // 2) Parse the required "Version" key value 190 // 191 if (key.Equals(VersionKey, StringComparison.OrdinalIgnoreCase)) 192 { 193 versionFound = true; 194 195 // Allow the version to include a 'v' or 'V' prefix... 196 if (value.Length > 0 && (value[0] == VersionValuePrefix || value[0] == 'V')) 197 { 198 value = value.Substring(1); 199 } 200 try 201 { 202 _version = Version.Parse(value); 203 } 204 catch (Exception e) 205 { 206 throw new ArgumentException(SR.Argument_FrameworkNameInvalidVersion, nameof(frameworkName), e); 207 } 208 } 209 // 210 // 3) Parse the optional "Profile" key value 211 // 212 else if (key.Equals(ProfileKey, StringComparison.OrdinalIgnoreCase)) 213 { 214 if (!string.IsNullOrEmpty(value)) 215 { 216 _profile = value; 217 } 218 } 219 else 220 { 221 throw new ArgumentException(SR.Argument_FrameworkNameInvalid, nameof(frameworkName)); 222 } 223 } 224 225 if (!versionFound) 226 { 227 throw new ArgumentException(SR.Argument_FrameworkNameMissingVersion, nameof(frameworkName)); 228 } 229 } 230 operator ==(FrameworkName left, FrameworkName right)231 public static bool operator ==(FrameworkName left, FrameworkName right) 232 { 233 if (object.ReferenceEquals(left, null)) 234 { 235 return object.ReferenceEquals(right, null); 236 } 237 return left.Equals(right); 238 } 239 operator !=(FrameworkName left, FrameworkName right)240 public static bool operator !=(FrameworkName left, FrameworkName right) 241 { 242 return !(left == right); 243 } 244 } 245 } 246