1 //------------------------------------------------------------------------------
2 // <copyright file="Unit.cs" company="Microsoft">
3 //     Copyright (c) Microsoft Corporation.  All rights reserved.
4 // </copyright>
5 //------------------------------------------------------------------------------
6 
7 namespace System.Web.UI.WebControls {
8 
9     using System;
10     using System.Globalization;
11     using System.ComponentModel;
12     using System.ComponentModel.Design;
13     using System.Security.Permissions;
14     using System.Web.Util;
15 
16     [
17         TypeConverterAttribute(typeof(UnitConverter))
18     ]
19     [Serializable]
20     public struct Unit {
21 
22 
23         /// <devdoc>
24         ///   Specifies an empty unit.
25         /// </devdoc>
26         public static readonly Unit Empty = new Unit();
27 
28         internal const int MaxValue = 32767;
29         internal const int MinValue = -32768;
30 
31         private readonly UnitType type;
32         private readonly double value;
33 
34 
35         /// <devdoc>
36         /// <para>Initializes a new instance of the <see cref='System.Web.UI.WebControls.Unit'/> structure with the specified 32-bit signed integer as
37         ///    the unit value and <see langword='Pixel'/> as the (default) unit type.</para>
38         /// </devdoc>
UnitSystem.Web.UI.WebControls.Unit39         public Unit(int value) {
40             if ((value < MinValue) || (value > MaxValue)) {
41                 throw new ArgumentOutOfRangeException("value");
42             }
43 
44             this.value = value;
45             this.type = UnitType.Pixel;
46         }
47 
48 
49         /// <devdoc>
50         /// <para> Initializes a new instance of the <see cref='System.Web.UI.WebControls.Unit'/> structure with the
51         ///    specified double-precision
52         ///    floating point number as the unit value and <see langword='Pixel'/>
53         ///    as the (default) unit type.</para>
54         /// </devdoc>
UnitSystem.Web.UI.WebControls.Unit55         public Unit(double value) {
56             if ((value < MinValue) || (value > MaxValue)) {
57                 throw new ArgumentOutOfRangeException("value");
58             }
59             this.value = (int)value;
60             this.type = UnitType.Pixel;
61         }
62 
63 
64         /// <devdoc>
65         /// <para>Initializes a new instance of the <see cref='System.Web.UI.WebControls.Unit'/> structure with the specified
66         ///    double-precision floating point number as the unit value and the specified
67         /// <see cref='System.Web.UI.WebControls.UnitType'/> as the unit type.</para>
68         /// </devdoc>
UnitSystem.Web.UI.WebControls.Unit69         public Unit(double value, UnitType type) {
70             if ((value < MinValue) || (value > MaxValue)) {
71                 throw new ArgumentOutOfRangeException("value");
72             }
73             if (type == UnitType.Pixel) {
74                 this.value = (int)value;
75             }
76             else {
77                 this.value = value;
78             }
79             this.type = type;
80         }
81 
82 
83         /// <devdoc>
84         /// <para>Initializes a new instance of the <see cref='System.Web.UI.WebControls.Unit'/> structure with the specified text
85         ///    string that contains the unit value and unit type. If the unit type is not
86         ///    specified, the default is <see langword='Pixel'/>
87         ///    . </para>
88         /// </devdoc>
UnitSystem.Web.UI.WebControls.Unit89         public Unit(string value) : this(value, CultureInfo.CurrentCulture, UnitType.Pixel) {
90         }
91 
92 
93         /// <devdoc>
94         ///    <para>[To be supplied.]</para>
95         /// </devdoc>
UnitSystem.Web.UI.WebControls.Unit96         public Unit(string value, CultureInfo culture) : this(value, culture, UnitType.Pixel) {
97         }
98 
UnitSystem.Web.UI.WebControls.Unit99         internal Unit(string value, CultureInfo culture, UnitType defaultType) {
100             if (String.IsNullOrEmpty(value)) {
101                 this.value = 0;
102                 this.type = (UnitType)0;
103             }
104             else {
105                 if (culture == null) {
106                     culture = CultureInfo.CurrentCulture;
107                 }
108 
109                 // This is invariant because it acts like an enum with a number together.
110                 // The enum part is invariant, but the number uses current culture.
111                 string trimLcase = value.Trim().ToLower(CultureInfo.InvariantCulture);
112                 int len = trimLcase.Length;
113 
114                 int lastDigit = -1;
115                 for (int i = 0; i < len; i++) {
116                     char ch = trimLcase[i];
117                     if (((ch < '0') || (ch > '9')) && (ch != '-') && (ch != '.') && (ch != ','))
118                         break;
119                     lastDigit = i;
120                 }
121                 if (lastDigit == -1) {
122                     throw new FormatException(SR.GetString(SR.UnitParseNoDigits, value));
123                 }
124                 if (lastDigit < len - 1) {
125                     type = (UnitType)GetTypeFromString(trimLcase.Substring(lastDigit+1).Trim());
126                 }
127                 else {
128                     type = defaultType;
129                 }
130 
131                 string numericPart = trimLcase.Substring(0, lastDigit+1);
132                 // Cannot use Double.FromString, because we don't use it in the ToString implementation
133                 try {
134                     TypeConverter converter = new SingleConverter();
135                     this.value = (Single)converter.ConvertFromString(null, culture, numericPart);
136 
137                     if (type == UnitType.Pixel) {
138                         this.value = (int)this.value;
139                     }
140                 }
141                 catch {
142                     throw new FormatException(SR.GetString(SR.UnitParseNumericPart, value, numericPart, type.ToString("G")));
143                 }
144                 if ((this.value < MinValue) || (this.value > MaxValue)) {
145                     throw new ArgumentOutOfRangeException("value");
146                 }
147             }
148         }
149 
150 
151         /// <devdoc>
152         /// <para>Gets a value indicating whether the <see cref='System.Web.UI.WebControls.Unit'/> is empty.</para>
153         /// </devdoc>
154         public bool IsEmpty {
155             get {
156                 return type == (UnitType)0;
157             }
158         }
159 
160 
161         /// <devdoc>
162         /// <para>Gets or sets the type of the <see cref='System.Web.UI.WebControls.Unit'/> .</para>
163         /// </devdoc>
164         public UnitType Type {
165             get {
166                 if (!IsEmpty) {
167                     return this.type;
168                 }
169                 else {
170                     return UnitType.Pixel;
171                 }
172             }
173         }
174 
175 
176         /// <devdoc>
177         /// <para>Gets the value of the <see cref='System.Web.UI.WebControls.Unit'/> .</para>
178         /// </devdoc>
179         public double Value {
180             get {
181                 return this.value;
182             }
183         }
184 
185 
186         /// <devdoc>
187         ///    <para>[To be supplied.]</para>
188         /// </devdoc>
GetHashCodeSystem.Web.UI.WebControls.Unit189         public override int GetHashCode() {
190             return HashCodeCombiner.CombineHashCodes(type.GetHashCode(), value.GetHashCode());
191         }
192 
193 
194         /// <devdoc>
195         /// <para>Compares this <see cref='System.Web.UI.WebControls.Unit'/> with the specified object.</para>
196         /// </devdoc>
EqualsSystem.Web.UI.WebControls.Unit197         public override bool Equals(object obj) {
198             if (obj == null || !(obj is Unit)) {
199                 return false;
200             }
201             Unit u = (Unit)obj;
202 
203             // compare internal values to avoid "defaulting" in the case of "Empty"
204             //
205             if (u.type == type && u.value == value) {
206                 return true;
207             }
208 
209             return false;
210         }
211 
212 
213         /// <devdoc>
214         ///    <para>Compares two units to find out if they have the same value and type.</para>
215         /// </devdoc>
operator ==System.Web.UI.WebControls.Unit216         public static bool operator ==(Unit left, Unit right) {
217 
218             // compare internal values to avoid "defaulting" in the case of "Empty"
219             //
220             return (left.type == right.type && left.value == right.value);
221         }
222 
223 
224         /// <devdoc>
225         ///    <para>Compares two units to find out if they have different
226         ///       values and/or types.</para>
227         /// </devdoc>
operator !=System.Web.UI.WebControls.Unit228         public static bool operator !=(Unit left, Unit right) {
229 
230             // compare internal values to avoid "defaulting" in the case of "Empty"
231             //
232             return (left.type != right.type || left.value != right.value);
233         }
234 
235 
236 
237         /// <devdoc>
238         ///  Converts UnitType to persistence string.
239         /// </devdoc>
GetStringFromTypeSystem.Web.UI.WebControls.Unit240         private static string GetStringFromType(UnitType type) {
241             switch (type) {
242                 case UnitType.Pixel:
243                     return "px";
244                 case UnitType.Point:
245                     return "pt";
246                 case UnitType.Pica:
247                     return "pc";
248                 case UnitType.Inch:
249                     return "in";
250                 case UnitType.Mm:
251                     return "mm";
252                 case UnitType.Cm:
253                     return "cm";
254                 case UnitType.Percentage:
255                     return "%";
256                 case UnitType.Em:
257                     return "em";
258                 case UnitType.Ex:
259                     return "ex";
260             }
261             return String.Empty;
262         }
263 
264 
265         /// <devdoc>
266         ///  Converts persistence string to UnitType.
267         /// </devdoc>
GetTypeFromStringSystem.Web.UI.WebControls.Unit268         private static UnitType GetTypeFromString(string value) {
269             if (!String.IsNullOrEmpty(value)) {
270                 if (value.Equals("px")) {
271                     return UnitType.Pixel;
272                 }
273                 else if (value.Equals("pt")) {
274                     return UnitType.Point;
275                 }
276                 else if (value.Equals("%")) {
277                     return UnitType.Percentage;
278                 }
279                 else if (value.Equals("pc")) {
280                     return UnitType.Pica;
281                 }
282                 else if (value.Equals("in")) {
283                     return UnitType.Inch;
284                 }
285                 else if (value.Equals("mm")) {
286                     return UnitType.Mm;
287                 }
288                 else if (value.Equals("cm")) {
289                     return UnitType.Cm;
290                 }
291                 else if (value.Equals("em")) {
292                     return UnitType.Em;
293                 }
294                 else if (value.Equals("ex")) {
295                     return UnitType.Ex;
296                 }
297                 else {
298                     throw new ArgumentOutOfRangeException("value");
299                 }
300             }
301             return UnitType.Pixel;
302         }
303 
304 
305         /// <devdoc>
306         ///    <para>[To be supplied.]</para>
307         /// </devdoc>
ParseSystem.Web.UI.WebControls.Unit308         public static Unit Parse(string s) {
309             return new Unit(s, CultureInfo.CurrentCulture);
310         }
311 
312 
313         /// <devdoc>
314         ///    <para>[To be supplied.]</para>
315         /// </devdoc>
ParseSystem.Web.UI.WebControls.Unit316         public static Unit Parse(string s, CultureInfo culture) {
317             return new Unit(s, culture);
318         }
319 
320 
321         /// <devdoc>
322         /// <para>Creates a <see cref='System.Web.UI.WebControls.Unit'/> of type <see langword='Percentage'/> from the specified 32-bit signed integer.</para>
323         /// </devdoc>
PercentageSystem.Web.UI.WebControls.Unit324         public static Unit Percentage(double n) {
325             return new Unit(n,UnitType.Percentage);
326         }
327 
328 
329         /// <devdoc>
330         /// <para>Creates a <see cref='System.Web.UI.WebControls.Unit'/> of type <see langword='Pixel'/> from the specified 32-bit signed integer.</para>
331         /// </devdoc>
PixelSystem.Web.UI.WebControls.Unit332         public static Unit Pixel(int n) {
333             return new Unit(n);
334         }
335 
336 
337         /// <devdoc>
338         /// <para>Creates a <see cref='System.Web.UI.WebControls.Unit'/> of type <see langword='Point'/> from the
339         ///    specified 32-bit signed integer.</para>
340         /// </devdoc>
PointSystem.Web.UI.WebControls.Unit341         public static Unit Point(int n) {
342             return new Unit(n,UnitType.Point);
343         }
344 
345 
346         /// <internalonly/>
347         /// <devdoc>
348         /// <para>Converts a <see cref='System.Web.UI.WebControls.Unit'/> to a <see cref='System.String' qualify='true'/> .</para>
349         /// </devdoc>
ToStringSystem.Web.UI.WebControls.Unit350         public override string ToString() {
351             return ToString((IFormatProvider)CultureInfo.CurrentCulture);
352         }
353 
354 
ToStringSystem.Web.UI.WebControls.Unit355         public string ToString(CultureInfo culture) {
356             return ToString((IFormatProvider)culture);
357         }
358 
359 
ToStringSystem.Web.UI.WebControls.Unit360         public string ToString(IFormatProvider formatProvider) {
361             if (IsEmpty)
362                 return String.Empty;
363 
364             // Double.ToString does not do the right thing, we get extra bits at the end
365             string valuePart;
366             if (type == UnitType.Pixel) {
367                 valuePart = ((int)value).ToString(formatProvider);
368             }
369             else {
370                 valuePart = ((float)value).ToString(formatProvider);
371             }
372 
373             return valuePart + Unit.GetStringFromType(type);
374         }
375 
376 
377         /// <devdoc>
378         /// <para>Implicitly creates a <see cref='System.Web.UI.WebControls.Unit'/> of type <see langword='Pixel'/> from the specified 32-bit unsigned integer.</para>
379         /// </devdoc>
operator UnitSystem.Web.UI.WebControls.Unit380         public static implicit operator Unit(int n) {
381             return Unit.Pixel(n);
382         }
383     }
384 }
385