Cappuccino  1.0.0
 All Classes Files Functions Variables Typedefs Macros Groups Pages
CPByteCountFormatter.j
Go to the documentation of this file.
1 /*
2  * CPByteCountFormatter.j
3  * Foundation
4  *
5  * Created by Aparajita Fishman.
6  * Copyright 2013, Cappuccino Foundation.
7  *
8  * This library is free software; you can redistribute it and/or
9  * modify it under the terms of the GNU Lesser General Public
10  * License as published by the Free Software Foundation; either
11  * version 2.1 of the License, or (at your option) any later version.
12  *
13  * This library is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16  * Lesser General Public License for more details.
17  *
18  * You should have received a copy of the GNU Lesser General Public
19  * License along with this library; if not, write to the Free Software
20  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
21  */
22 
23 
24 
25 // Allowed units
34 
35 // Note: The Cocoa documentation says File is binary, but in practice it's decimal
40 
41 var CPByteCountFormatterUnits = [ @"bytes", @"KB", @"MB", @"GB", @"TB", @"PB" ];
42 
43 
50 @implementation CPByteCountFormatter : CPFormatter
51 {
52  int _countStyle;
53  BOOL _allowsNonnumericFormatting;
54  BOOL _includesActualByteCount;
55  BOOL _includesCount;
56  BOOL _includesUnit;
57  BOOL _adaptive;
58  BOOL _zeroPadsFractionDigits;
59  int _allowedUnits;
60  CPNumberFormatter _numberFormatter;
61 }
62 
63 - (id)init
64 {
65  if (self = [super init])
66  {
67  _adaptive = YES;
68  _allowedUnits = CPByteCountFormatterUseDefault;
69  _allowsNonnumericFormatting = YES;
71  _includesActualByteCount = NO;
72  _includesCount = YES;
73  _includesUnit = YES;
74  _zeroPadsFractionDigits = NO;
75  _numberFormatter = [CPNumberFormatter new];
76  [_numberFormatter setNumberStyle:CPNumberFormatterDecimalStyle];
77  [_numberFormatter setMinimumFractionDigits:0];
78  }
79 
80  return self;
81 }
82 
85 + (CPString)stringFromByteCount:(int)byteCount countStyle:(int)countStyle
86 {
87  var formatter = [CPByteCountFormatter new];
88 
89  [formatter setCountStyle:countStyle];
90 
91  return [formatter stringFromByteCount:byteCount];
92 }
93 
94 - (CPString)stringFromByteCount:(int)byteCount
95 {
96  var divisor,
97  exponent = 0,
98  unitIndex = ((_allowedUnits === 0) || (_allowedUnits & CPByteCountFormatterUseBytes)) ? 0 : -1,
99  bytes = byteCount,
100  unitBytes = bytes,
101  unitCount = [CPByteCountFormatterUnits count];
102 
103  if (_countStyle === CPByteCountFormatterCountStyleFile ||
105  divisor = 1000;
106  else
107  divisor = 1024;
108 
109  while ((bytes >= divisor) && (exponent < unitCount))
110  {
111  bytes /= divisor;
112  ++exponent;
113 
114  // If there is a valid unit for this exponent,
115  // update the unit we will use and the byte count for that unit
116  if (_allowedUnits === 0 || (_allowedUnits & (1 << exponent)))
117  {
118  unitIndex = exponent;
119  unitBytes = bytes;
120  }
121  }
122 
123  /*
124  If no allowed unit was found before bytes < divisor,
125  keep dividing until we find an allowed unit. We can skip
126  bytes, if that is allowed unit, unitIndex will be >= 0.
127  */
128  if (unitIndex === -1)
129  for (var i = 1; i < unitCount; ++i)
130  {
131  unitBytes /= divisor;
132 
133  if ((_allowedUnits === 0) || (_allowedUnits & (1 << i)))
134  {
135  unitIndex = i;
136  break;
137  }
138  }
139 
140  var minDigits = 0,
141  maxDigits = CPDecimalNoScale;
142 
143  // Fractional units get as many digits as they need
144  if (unitBytes >= 1.0)
145  {
146  if (_adaptive)
147  {
148  // 0 fraction digits for bytes and K, 1 fraction digit for MB, 2 digits for GB and above
149  var digits;
150 
151  if (exponent <= 1)
152  digits = 0;
153  else if (exponent == 2)
154  digits = 1;
155  else
156  digits = 2;
157 
158  maxDigits = digits;
159 
160  if (_zeroPadsFractionDigits)
161  minDigits = digits;
162  }
163  else
164  {
165  if (_zeroPadsFractionDigits)
166  minDigits = 2;
167 
168  if (bytes >= 1)
169  maxDigits = 2;
170  }
171  }
172 
173  [_numberFormatter setMinimumFractionDigits:minDigits];
174  [_numberFormatter setMaximumFractionDigits:maxDigits];
175 
176  var parts = [];
177 
178  if (_includesCount)
179  {
180  if (_allowsNonnumericFormatting && bytes === 0)
181  [parts addObject:@"Zero"];
182  else
183  [parts addObject:[_numberFormatter stringFromNumber:unitBytes]];
184  }
185 
186  if (_includesUnit)
187  [parts addObject:CPByteCountFormatterUnits[unitIndex]];
188 
189  if ((unitIndex > 0) && _includesCount && _includesUnit && _includesActualByteCount)
190  {
191  [_numberFormatter setMaximumFractionDigits:0];
192  [parts addObject:[CPString stringWithFormat:@"(%s bytes)", [_numberFormatter stringFromNumber:byteCount]]];
193  }
194 
195  var result = [parts componentsJoinedByString:@" "];
196 
197  if (byteCount === 1)
198  return [result stringByReplacingOccurrencesOfString:@"bytes" withString:@"byte"];
199  else
200  return result;
201 }
202 
206 - (CPString)stringForObjectValue:(id)anObject
207 {
208  if ([anObject isKindOfClass:CPNumber])
209  return [self stringFromByteCount:anObject];
210  else
211  return nil;
212 }
213 
214 - (BOOL)getObjectValue:(idRef)anObject forString:(CPString)aString errorDescription:(CPStringRef)anError
215 {
216  // Not implemented
217  return NO;
218 }
219 
222 - (int)countStyle
223 {
224  return _countStyle;
225 }
226 
227 - (void)setCountStyle:(int)style
228 {
229  _countStyle = style;
230 }
231 
232 - (BOOL)allowsNonnumericFormatting
233 {
234  return _allowsNonnumericFormatting;
235 }
236 
237 - (void)setAllowsNonnumericFormatting:(BOOL)shouldAllowNonnumericFormatting
238 {
239  _allowsNonnumericFormatting = shouldAllowNonnumericFormatting;
240 }
241 
242 - (BOOL)includesActualByteCount
243 {
244  return _includesActualByteCount;
245 }
246 
247 - (void)setIncludesActualByteCount:(BOOL)shouldIncludeActualByteCount
248 {
249  _includesActualByteCount = shouldIncludeActualByteCount;
250 }
251 
252 - (BOOL)isAdaptive
253 {
254  return _adaptive;
255 }
256 
257 - (void)setAdaptive:(BOOL)shouldBeAdaptive
258 {
259  _adaptive = shouldBeAdaptive;
260 }
261 
262 - (int)allowedUnits
263 {
264  return _allowedUnits;
265 }
266 
267 - (void)setAllowedUnits:(int)allowed
268 {
269  // Note: CPByteCountFormatterUseDefault is equivalent to UseAll
270  _allowedUnits = allowed;
271 }
272 
273 - (BOOL)includesCount
274 {
275  return _includesCount;
276 }
277 
278 - (void)setIncludesCount:(BOOL)shouldIncludeCount
279 {
280  _includesCount = shouldIncludeCount;
281 }
282 
283 - (BOOL)includesUnit
284 {
285  return _includesUnit;
286 }
287 
288 - (void)setIncludesUnit:(BOOL)shouldIncludeUnit
289 {
290  _includesUnit = shouldIncludeUnit;
291 }
292 
293 - (BOOL)zeroPadsFractionDigits
294 {
295  return _zeroPadsFractionDigits;
296 }
297 
298 - (void)setZeroPadsFractionDigits:(BOOL)shouldZeroPad
299 {
300  _zeroPadsFractionDigits = shouldZeroPad;
301 }
302 
303 @end
304 
305 
306 var CPByteCountFormatterCountStyleKey = @"CPByteCountFormatterCountStyleKey",
307  CPByteCountFormatterAllowsNonnumericFormattingKey = @"CPByteCountFormatterAllowsNonnumericFormattingKey",
308  CPByteCountFormatterIncludesActualByteCountKey = @"CPByteCountFormatterIncludesActualByteCountKey",
309  CPByteCountFormatterIncludesCountKey = @"CPByteCountFormatterIncludesCountKey",
310  CPByteCountFormatterIncludesUnitKey = @"CPByteCountFormatterIncludesUnitKey",
311  CPByteCountFormatterAdaptiveKey = @"CPByteCountFormatterAdaptiveKey",
312  CPByteCountFormatterZeroPadsFractionDigitsKey = @"CPByteCountFormatterZeroPadsFractionDigitsKey",
313  CPByteCountFormatterAllowedUnitsKey = @"CPByteCountFormatterAllowedUnitsKey";
314 
316 
317 - (id)initWithCoder:(CPCoder)aCoder
318 {
319  self = [super initWithCoder:aCoder];
320 
321  if (self)
322  {
323  _countStyle = [aCoder decodeIntForKey:CPByteCountFormatterCountStyleKey];
324  _allowsNonnumericFormatting = [aCoder decodeBoolForKey:CPByteCountFormatterAllowsNonnumericFormattingKey];
325  _includesActualByteCount = [aCoder decodeBoolForKey:CPByteCountFormatterIncludesActualByteCountKey];
326  _includesCount = [aCoder decodeBoolForKey:CPByteCountFormatterIncludesCountKey];
327  _includesUnit = [aCoder decodeBoolForKey:CPByteCountFormatterIncludesUnitKey];
328  _adaptive = [aCoder decodeBoolForKey:CPByteCountFormatterAdaptiveKey];
329  _zeroPadsFractionDigits = [aCoder decodeBoolForKey:CPByteCountFormatterZeroPadsFractionDigitsKey];
330  _allowedUnits = [aCoder decodeIntForKey:CPByteCountFormatterAllowedUnitsKey];
331  }
332 
333  return self;
334 }
335 
336 - (void)encodeWithCoder:(CPCoder)aCoder
337 {
338  [super encodeWithCoder:aCoder];
339 
340  [aCoder encodeInt:_countStyle forKey:CPByteCountFormatterCountStyleKey];
341  [aCoder encodeBool:_allowsNonnumericFormatting forKey:CPByteCountFormatterAllowsNonnumericFormattingKey];
342  [aCoder encodeBool:_includesActualByteCount forKey:CPByteCountFormatterIncludesActualByteCountKey];
343  [aCoder encodeBool:_includesCount forKey:CPByteCountFormatterIncludesCountKey];
344  [aCoder encodeBool:_includesUnit forKey:CPByteCountFormatterIncludesUnitKey];
345  [aCoder encodeBool:_adaptive forKey:CPByteCountFormatterAdaptiveKey];
346  [aCoder encodeBool:_zeroPadsFractionDigits forKey:CPByteCountFormatterZeroPadsFractionDigitsKey];
347  [aCoder encodeInt:_allowedUnits forKey:CPByteCountFormatterAllowedUnitsKey];
348 }
349 
350 @end