API  0.9.7
 All Classes Files Functions Variables 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 
51 @implementation CPByteCountFormatter : CPFormatter
52 {
53  int _countStyle;
54  BOOL _allowsNonnumericFormatting;
55  BOOL _includesActualByteCount;
56  BOOL _includesCount;
57  BOOL _includesUnit;
58  BOOL _adaptive;
59  BOOL _zeroPadsFractionDigits;
60  int _allowedUnits;
61  CPNumberFormatter _numberFormatter;
62 }
63 
64 - (id)init
65 {
66  if (self = [super init])
67  {
68  _adaptive = YES;
69  _allowedUnits = CPByteCountFormatterUseDefault;
70  _allowsNonnumericFormatting = YES;
72  _includesActualByteCount = NO;
73  _includesCount = YES;
74  _includesUnit = YES;
75  _zeroPadsFractionDigits = NO;
76  _numberFormatter = [CPNumberFormatter new];
77  [_numberFormatter setNumberStyle:CPNumberFormatterDecimalStyle];
78  [_numberFormatter setMinimumFractionDigits:0];
79  }
80 
81  return self;
82 }
83 
86 + (CPString)stringFromByteCount:(int)byteCount countStyle:(int)countStyle
87 {
88  var formatter = [CPByteCountFormatter new];
89 
90  [formatter setCountStyle:countStyle];
91 
92  return [formatter stringFromByteCount:byteCount];
93 }
94 
95 - (CPString)stringFromByteCount:(int)byteCount
96 {
97  var divisor,
98  exponent = 0,
99  unitIndex = ((_allowedUnits === 0) || (_allowedUnits & CPByteCountFormatterUseBytes)) ? 0 : -1,
100  bytes = byteCount,
101  unitBytes = bytes,
102  unitCount = [CPByteCountFormatterUnits count];
103 
104  if (_countStyle === CPByteCountFormatterCountStyleFile ||
106  divisor = 1000;
107  else
108  divisor = 1024;
109 
110  while ((bytes >= divisor) && (exponent < unitCount))
111  {
112  bytes /= divisor;
113  ++exponent;
114 
115  // If there is a valid unit for this exponent,
116  // update the unit we will use and the byte count for that unit
117  if (_allowedUnits === 0 || (_allowedUnits & (1 << exponent)))
118  {
119  unitIndex = exponent;
120  unitBytes = bytes;
121  }
122  }
123 
124  /*
125  If no allowed unit was found before bytes < divisor,
126  keep dividing until we find an allowed unit. We can skip
127  bytes, if that is allowed unit, unitIndex will be >= 0.
128  */
129  if (unitIndex === -1)
130  for (var i = 1; i < unitCount; ++i)
131  {
132  unitBytes /= divisor;
133 
134  if ((_allowedUnits === 0) || (_allowedUnits & (1 << i)))
135  {
136  unitIndex = i;
137  break;
138  }
139  }
140 
141  var minDigits = 0,
142  maxDigits = CPDecimalNoScale;
143 
144  // Fractional units get as many digits as they need
145  if (unitBytes >= 1.0)
146  {
147  if (_adaptive)
148  {
149  // 0 fraction digits for bytes and K, 1 fraction digit for MB, 2 digits for GB and above
150  var digits;
151 
152  if (exponent <= 1)
153  digits = 0;
154  else if (exponent == 2)
155  digits = 1;
156  else
157  digits = 2;
158 
159  maxDigits = digits;
160 
161  if (_zeroPadsFractionDigits)
162  minDigits = digits;
163  }
164  else
165  {
166  if (_zeroPadsFractionDigits)
167  minDigits = 2;
168 
169  if (bytes >= 1)
170  maxDigits = 2;
171  }
172  }
173 
174  [_numberFormatter setMinimumFractionDigits:minDigits];
175  [_numberFormatter setMaximumFractionDigits:maxDigits];
176 
177  var parts = [];
178 
179  if (_includesCount)
180  {
181  if (_allowsNonnumericFormatting && bytes === 0)
182  [parts addObject:@"Zero"];
183  else
184  [parts addObject:[_numberFormatter stringFromNumber:unitBytes]];
185  }
186 
187  if (_includesUnit)
188  [parts addObject:CPByteCountFormatterUnits[unitIndex]];
189 
190  if ((unitIndex > 0) && _includesCount && _includesUnit && _includesActualByteCount)
191  {
192  [_numberFormatter setMaximumFractionDigits:0];
193  [parts addObject:[CPString stringWithFormat:@"(%s bytes)", [_numberFormatter stringFromNumber:byteCount]]];
194  }
195 
196  var result = [parts componentsJoinedByString:@" "];
197 
198  if (byteCount === 1)
199  return [result stringByReplacingOccurrencesOfString:@"bytes" withString:@"byte"];
200  else
201  return result;
202 }
203 
207 - (CPString)stringForObjectValue:(id)anObject
208 {
209  if ([anObject isKindOfClass:CPNumber])
210  return [self stringFromByteCount:anObject];
211  else
212  return nil;
213 }
214 
215 - (BOOL)getObjectValue:(idRef)anObject forString:(CPString)aString errorDescription:(CPStringRef)anError
216 {
217  // Not implemented
218  return NO;
219 }
220 
223 - (int)countStyle
224 {
225  return _countStyle;
226 }
227 
228 - (void)setCountStyle:(int)style
229 {
230  _countStyle = style;
231 }
232 
233 - (BOOL)allowsNonnumericFormatting
234 {
235  return _allowsNonnumericFormatting;
236 }
237 
238 - (void)setAllowsNonnumericFormatting:(BOOL)shouldAllowNonnumericFormatting
239 {
240  _allowsNonnumericFormatting = shouldAllowNonnumericFormatting;
241 }
242 
243 - (BOOL)includesActualByteCount
244 {
245  return _includesActualByteCount;
246 }
247 
248 - (void)setIncludesActualByteCount:(BOOL)shouldIncludeActualByteCount
249 {
250  _includesActualByteCount = shouldIncludeActualByteCount;
251 }
252 
253 - (BOOL)isAdaptive
254 {
255  return _adaptive;
256 }
257 
258 - (void)setAdaptive:(BOOL)shouldBeAdaptive
259 {
260  _adaptive = shouldBeAdaptive;
261 }
262 
263 - (int)allowedUnits
264 {
265  return _allowedUnits;
266 }
267 
268 - (void)setAllowedUnits:(int)allowed
269 {
270  // Note: CPByteCountFormatterUseDefault is equivalent to UseAll
271  _allowedUnits = allowed;
272 }
273 
274 - (BOOL)includesCount
275 {
276  return _includesCount;
277 }
278 
279 - (void)setIncludesCount:(BOOL)shouldIncludeCount
280 {
281  _includesCount = shouldIncludeCount;
282 }
283 
284 - (BOOL)includesUnit
285 {
286  return _includesUnit;
287 }
288 
289 - (void)setIncludesUnit:(BOOL)shouldIncludeUnit
290 {
291  _includesUnit = shouldIncludeUnit;
292 }
293 
294 - (BOOL)zeroPadsFractionDigits
295 {
296  return _zeroPadsFractionDigits;
297 }
298 
299 - (void)setZeroPadsFractionDigits:(BOOL)shouldZeroPad
300 {
301  _zeroPadsFractionDigits = shouldZeroPad;
302 }
303 
304 @end
305 
306 
307 var CPByteCountFormatterCountStyleKey = @"CPByteCountFormatterCountStyleKey",
308  CPByteCountFormatterAllowsNonnumericFormattingKey = @"CPByteCountFormatterAllowsNonnumericFormattingKey",
309  CPByteCountFormatterIncludesActualByteCountKey = @"CPByteCountFormatterIncludesActualByteCountKey",
310  CPByteCountFormatterIncludesCountKey = @"CPByteCountFormatterIncludesCountKey",
311  CPByteCountFormatterIncludesUnitKey = @"CPByteCountFormatterIncludesUnitKey",
312  CPByteCountFormatterAdaptiveKey = @"CPByteCountFormatterAdaptiveKey",
313  CPByteCountFormatterZeroPadsFractionDigitsKey = @"CPByteCountFormatterZeroPadsFractionDigitsKey",
314  CPByteCountFormatterAllowedUnitsKey = @"CPByteCountFormatterAllowedUnitsKey";
315 
317 
318 - (id)initWithCoder:(CPCoder)aCoder
319 {
320  self = [super initWithCoder:aCoder];
321 
322  if (self)
323  {
324  _countStyle = [aCoder decodeIntForKey:CPByteCountFormatterCountStyleKey];
325  _allowsNonnumericFormatting = [aCoder decodeBoolForKey:CPByteCountFormatterAllowsNonnumericFormattingKey];
326  _includesActualByteCount = [aCoder decodeBoolForKey:CPByteCountFormatterIncludesActualByteCountKey];
327  _includesCount = [aCoder decodeBoolForKey:CPByteCountFormatterIncludesCountKey];
328  _includesUnit = [aCoder decodeBoolForKey:CPByteCountFormatterIncludesUnitKey];
329  _adaptive = [aCoder decodeBoolForKey:CPByteCountFormatterAdaptiveKey];
330  _zeroPadsFractionDigits = [aCoder decodeBoolForKey:CPByteCountFormatterZeroPadsFractionDigitsKey];
331  _allowedUnits = [aCoder decodeIntForKey:CPByteCountFormatterAllowedUnitsKey];
332  }
333 
334  return self;
335 }
336 
337 - (void)encodeWithCoder:(CPCoder)aCoder
338 {
339  [super encodeWithCoder:aCoder];
340 
341  [aCoder encodeInt:_countStyle forKey:CPByteCountFormatterCountStyleKey];
342  [aCoder encodeBool:_allowsNonnumericFormatting forKey:CPByteCountFormatterAllowsNonnumericFormattingKey];
343  [aCoder encodeBool:_includesActualByteCount forKey:CPByteCountFormatterIncludesActualByteCountKey];
344  [aCoder encodeBool:_includesCount forKey:CPByteCountFormatterIncludesCountKey];
345  [aCoder encodeBool:_includesUnit forKey:CPByteCountFormatterIncludesUnitKey];
346  [aCoder encodeBool:_adaptive forKey:CPByteCountFormatterAdaptiveKey];
347  [aCoder encodeBool:_zeroPadsFractionDigits forKey:CPByteCountFormatterZeroPadsFractionDigitsKey];
348  [aCoder encodeInt:_allowedUnits forKey:CPByteCountFormatterAllowedUnitsKey];
349 }
350 
351 @end