Merge pull request #1458 from BrzVlad/feature-fat-cas
[mono.git] / mcs / class / System.Web / System.Web.UI.WebControls / Unit.cs
1 //
2 // System.Web.UI.WebControls.Unit.cs
3 //
4 // Authors:
5 //   Miguel de Icaza (miguel@novell.com)
6 //   Ben Maurer (bmaurer@ximian.com).
7 //
8 // Copyright (C) 2005 Novell, Inc (http://www.novell.com)
9 //
10 // Permission is hereby granted, free of charge, to any person obtaining
11 // a copy of this software and associated documentation files (the
12 // "Software"), to deal in the Software without restriction, including
13 // without limitation the rights to use, copy, modify, merge, publish,
14 // distribute, sublicense, and/or sell copies of the Software, and to
15 // permit persons to whom the Software is furnished to do so, subject to
16 // the following conditions:
17 // 
18 // The above copyright notice and this permission notice shall be
19 // included in all copies or substantial portions of the Software.
20 // 
21 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
22 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
23 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
24 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
25 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
26 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
27 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
28 //
29
30 using System.Globalization;
31 using System.ComponentModel;
32 using System.Security.Permissions;
33 using System.Web.Util;
34
35 namespace System.Web.UI.WebControls {
36
37         [TypeConverter(typeof (UnitConverter))]
38         [Serializable]
39         public struct Unit {
40                 enum ParsingStage
41                 {
42                         Trim,
43                         SignOrSep,
44                         DigitOrSep,
45                         DigitOrUnit,
46                         Unit
47                 }
48                 
49                 UnitType type;
50                 double value;
51                 bool valueSet;
52                 public static readonly Unit Empty;
53                 
54                 public Unit (double value, UnitType type)
55                 {
56                         if (value < -32768 || value > 32767)
57                                 throw new ArgumentOutOfRangeException ("value");
58
59                         this.type = type;
60                         if (type == UnitType.Pixel)
61                                 this.value = (int) value;
62                         else
63                                 this.value = value;
64                         valueSet = true;
65                 }
66
67                 public Unit (double value) : this (value, UnitType.Pixel)
68                 {
69                 }
70                 
71                 public Unit (int value) : this ((double) value, UnitType.Pixel)
72                 {
73                 }
74
75                 internal Unit (string input, char sep)
76                 {
77                         if (input == null || input == String.Empty){
78                                 type = (UnitType) 0;
79                                 value = 0.0;
80                                 valueSet = false;
81                                 return;
82                         }
83
84                         value = 0.0;
85                         double dv = 0, factor = .1;
86                         int i = 0;
87                         int count = input.Length;
88                         int sign = 1, unitStart = -1, unitLen = 0, wsCount = 0;
89                         char c;
90                         ParsingStage ps = ParsingStage.Trim;
91                         bool done = false, haveSep = false, haveDigits = false, isWhiteSpace;
92
93                         while (!done && i < count) {
94                                 c = input [i];
95
96                                 switch (ps) {
97                                         case ParsingStage.Trim:
98                                                 if (Char.IsWhiteSpace (c)) {
99                                                         i++;
100                                                         continue;
101                                                 }
102                                                 ps = ParsingStage.SignOrSep;
103                                                 continue;
104
105                                         case ParsingStage.SignOrSep:
106                                                 wsCount = 0;
107                                                 if (c == '-') {
108                                                         sign = -1;
109                                                         i++;
110                                                         ps = ParsingStage.DigitOrSep;
111                                                         continue;
112                                                 }
113
114                                                 if (c == sep) {
115                                                         i++;
116                                                         haveSep = true;
117                                                         ps = ParsingStage.DigitOrUnit;
118                                                         dv = 0;
119                                                         continue;
120                                                 }
121
122                                                 if (Char.IsDigit (c)) {
123                                                         ps = ParsingStage.DigitOrSep;
124                                                         continue;
125                                                 }
126           
127                                                 throw new FormatException ();
128
129                                         case ParsingStage.DigitOrSep:
130                                                 if (Char.IsDigit (c)) {
131                                                         dv = dv * 10 + ((int) c) - ((int)'0');
132                                                         i++;
133                                                         haveDigits = true;
134                                                         continue;
135                                                 }
136
137                                                 if (c == sep) {
138                                                         if (wsCount > 0)
139                                                                 throw new ArgumentOutOfRangeException ("input");
140             
141                                                         i++;
142                                                         haveSep = true;
143                                                         value = dv * sign;
144                                                         dv = 0;
145                                                         ps = ParsingStage.DigitOrUnit;
146                                                         continue;
147                                                 }
148
149                                                 isWhiteSpace = Char.IsWhiteSpace (c);
150                                                 if (isWhiteSpace || c == '%' || Char.IsLetter (c)) {
151                                                         if (isWhiteSpace) {
152                                                                 if (!haveDigits)
153                                                                         throw new ArgumentOutOfRangeException ("input");
154                                                                 wsCount++;
155                                                                 i++;
156                                                                 continue;
157                                                         }
158
159                                                         value = dv * sign;
160                                                         dv = 0;
161                                                         unitStart = i;
162             
163                                                         if (haveSep) {
164                                                                 haveDigits = false;
165                                                                 ps = ParsingStage.DigitOrUnit;
166                                                         } else
167                                                                 ps = ParsingStage.Unit;
168                                                         wsCount = 0;
169                                                         continue;
170                                                 }
171           
172                                                 throw new FormatException ();
173           
174                                         case ParsingStage.DigitOrUnit:
175                                                 if (c == '%') {
176                                                         unitStart = i;
177                                                         unitLen = 1;
178                                                         done = true;
179                                                         continue;
180                                                 }
181
182                                                 isWhiteSpace = Char.IsWhiteSpace (c);
183                                                 if (isWhiteSpace || Char.IsLetter (c)) {
184                                                         if (isWhiteSpace) {
185                                                                 wsCount++;
186                                                                 i++;
187                                                                 continue;
188                                                         }
189             
190                                                         ps = ParsingStage.Unit;
191                                                         unitStart = i;
192                                                         continue;
193                                                 }
194
195                                                 if (Char.IsDigit (c)) {
196                                                         if (wsCount > 0)
197                                                                 throw new ArgumentOutOfRangeException ();
198             
199                                                         dv = dv + (((int) c) - ((int) '0')) * factor;
200                                                         factor = factor *.1;
201                                                         i++;
202                                                         continue;
203                                                 }
204           
205                                                 throw new FormatException ();
206
207                                         case ParsingStage.Unit:
208                                                 if (c == '%' || Char.IsLetter (c)) {
209                                                         i++;
210                                                         unitLen++;
211                                                         continue;
212                                                 }
213
214                                                 if (unitLen == 0 && Char.IsWhiteSpace (c)) {
215                                                         i++;
216                                                         unitStart++;
217                                                         continue;
218                                                 }
219           
220                                                 done = true;
221                                                 break;
222                                 }
223                         }
224
225                         value += dv * sign;
226                         if (unitStart >= 0) {
227                                 int unitTail = unitStart + unitLen;
228                                 if (unitTail < count) {
229                                         for (int j = unitTail; j < count; j++) {
230                                                 if (!Char.IsWhiteSpace (input [j]))
231                                                         throw new ArgumentOutOfRangeException ("input");
232                                         }
233                                 }
234
235                                 if (unitLen == 1 && input [unitStart] == '%')
236                                         type = UnitType.Percentage;
237                                 else {
238                                         switch (input.Substring (unitStart, unitLen).ToLower (Helpers.InvariantCulture)) {
239                                                 case "in": type = UnitType.Inch; break;
240                                                 case "cm": type = UnitType.Cm; break;
241                                                 case "mm": type = UnitType.Mm; break;
242                                                 case "pt": type = UnitType.Point; break;
243                                                 case "pc": type = UnitType.Pica; break;
244                                                 case "em": type = UnitType.Em; break;
245                                                 case "ex": type = UnitType.Ex; break;
246                                                 case "px":
247                                                         type = UnitType.Pixel;
248                                                         break;
249                                                 default:
250                                                         throw new ArgumentOutOfRangeException ("value");
251                                         }
252                                 }
253                         } else
254                                 type = UnitType.Pixel;
255
256                         if (haveSep && type == UnitType.Pixel)
257                                 throw new FormatException ("Pixel units do not allow floating point values");
258                         valueSet = true;
259                 }
260                 
261                 public Unit (string value) : this (value, '.')
262                 {
263                 }
264
265                 public Unit (string value, CultureInfo culture) : this (value, culture.NumberFormat.NumberDecimalSeparator [0])
266                 {
267                 }
268
269                 internal Unit (string value, CultureInfo culture, UnitType t) : this (value, '.')
270                 {
271                 }
272                 
273                 public bool IsEmpty {
274                         get {
275                                 return type == 0;
276                         }
277                 }
278
279                 public UnitType Type {
280                         get {
281                                 if (type == 0)
282                                         return UnitType.Pixel;
283                                 return type;
284                         }
285                 }
286
287                 public double Value {
288                         get {
289                                 return value;
290                         }
291                 }
292                 
293                 public static Unit Parse (string s)
294                 {
295                         return new Unit (s);
296                 }
297
298                 public static System.Web.UI.WebControls.Unit Parse (string s, System.Globalization.CultureInfo culture)
299                 {
300                         return new Unit (s, culture);
301                 }
302                 
303
304                 public static Unit Percentage (double n)
305                 {
306                         return new Unit (n, UnitType.Percentage);
307                 }
308                 
309                 public static Unit Pixel (int n)
310                 {
311                         return new Unit (n);
312                 }
313                 
314                 public static Unit Point (int n)
315                 {
316                         return new Unit (n, UnitType.Point);
317                 }
318                                 
319                 public override bool Equals (object obj)
320                 {
321                         if (obj is Unit){
322                                 Unit other = (Unit) obj;
323                                 return (other.type == type && other.value == value && valueSet == other.valueSet);
324                         }
325                         return false;
326                 }
327                 
328                 public override int GetHashCode ()
329                 {
330                         return Type.GetHashCode () ^ Value.GetHashCode ();
331                 }
332                 
333                 public static bool operator == (Unit left, Unit right)
334                 {
335                         return left.Type == right.Type && left.Value == right.Value && left.valueSet == right.valueSet;
336                 }
337
338                 public static bool operator != (Unit left, Unit right)
339                 {
340                         return left.Type != right.Type || left.Value != right.Value || left.valueSet != right.valueSet;
341                 }
342                 
343                 public static implicit operator Unit (int n)
344                 {
345                         return new Unit (n);
346                 }
347
348                 internal static string GetExtension (UnitType type)
349                 {
350                         switch (type){
351                                 case UnitType.Pixel: return "px";
352                                 case UnitType.Point: return "pt";
353                                 case UnitType.Pica: return "pc";
354                                 case UnitType.Inch: return "in";
355                                 case UnitType.Mm: return "mm";
356                                 case UnitType.Cm: return "cm";
357                                 case UnitType.Percentage: return "%";
358                                 case UnitType.Em: return "em";
359                                 case UnitType.Ex: return "ex";
360                                 default: return String.Empty;
361                         }
362                 }
363
364                 public string ToString (CultureInfo culture)
365                 {
366                         if (type == 0)
367                                 return String.Empty;
368                         
369                         string ex = GetExtension (type);
370                         
371                         return value.ToString (culture) + ex;
372                 }
373                         
374                 public override string ToString ()
375                 {
376                         return ToString (Helpers.InvariantCulture);
377                 }
378
379                 public string ToString (IFormatProvider provider)
380                 {
381                         if (type == 0)
382                                 return String.Empty;
383
384                         string ex = GetExtension (type);
385
386                         return value.ToString (provider) + ex;
387                 }
388         }
389
390 }