View Javadoc
1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one
3    * or more contributor license agreements.  See the NOTICE file
4    * distributed with this work for additional information
5    * regarding copyright ownership.  The ASF licenses this file
6    * to you under the Apache License, Version 2.0 (the
7    * "License"); you may not use this file except in compliance
8    * with the License.  You may obtain a copy of the License at
9    *
10   *   http://www.apache.org/licenses/LICENSE-2.0
11   *
12   * Unless required by applicable law or agreed to in writing,
13   * software distributed under the License is distributed on an
14   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15   * KIND, either express or implied.  See the License for the
16   * specific language governing permissions and limitations
17   * under the License.
18   */
19  package org.apache.maven.cli.transfer;
20  
21  import org.apache.maven.api.services.MessageBuilder;
22  
23  /**
24   * Formats file size with the associated <a href="https://en.wikipedia.org/wiki/Metric_prefix">SI</a> prefix
25   * (GB, MB, kB) and using the patterns <code>#0.0</code> for numbers between 1 and 10
26   * and <code>###0</code> for numbers between 10 and 1000+ by default.
27   *
28   * @see <a href="https://en.wikipedia.org/wiki/Metric_prefix">https://en.wikipedia.org/wiki/Metric_prefix</a>
29   * @see <a href="https://en.wikipedia.org/wiki/Binary_prefix">https://en.wikipedia.org/wiki/Binary_prefix</a>
30   * @see <a
31   *      href="https://en.wikipedia.org/wiki/Octet_%28computing%29">https://en.wikipedia.org/wiki/Octet_(computing)</a>
32   */
33  public class FileSizeFormat {
34      public enum ScaleUnit {
35          BYTE {
36              @Override
37              public long bytes() {
38                  return 1L;
39              }
40  
41              @Override
42              public String symbol() {
43                  return "B";
44              }
45          },
46          KILOBYTE {
47              @Override
48              public long bytes() {
49                  return 1000L;
50              }
51  
52              @Override
53              public String symbol() {
54                  return "kB";
55              }
56          },
57          MEGABYTE {
58              @Override
59              public long bytes() {
60                  return KILOBYTE.bytes() * KILOBYTE.bytes();
61              }
62  
63              @Override
64              public String symbol() {
65                  return "MB";
66              }
67          },
68          GIGABYTE {
69              @Override
70              public long bytes() {
71                  return MEGABYTE.bytes() * KILOBYTE.bytes();
72              }
73              ;
74  
75              @Override
76              public String symbol() {
77                  return "GB";
78              }
79          };
80  
81          public abstract long bytes();
82  
83          public abstract String symbol();
84  
85          public static ScaleUnit getScaleUnit(long size) {
86              if (size < 0L) {
87                  throw new IllegalArgumentException("file size cannot be negative: " + size);
88              }
89  
90              if (size >= GIGABYTE.bytes()) {
91                  return GIGABYTE;
92              } else if (size >= MEGABYTE.bytes()) {
93                  return MEGABYTE;
94              } else if (size >= KILOBYTE.bytes()) {
95                  return KILOBYTE;
96              } else {
97                  return BYTE;
98              }
99          }
100     }
101 
102     public String format(long size) {
103         return format(size, null);
104     }
105 
106     public String format(long size, ScaleUnit unit) {
107         return format(size, unit, false);
108     }
109 
110     public String format(long size, ScaleUnit unit, boolean omitSymbol) {
111         StringBuilder sb = new StringBuilder();
112         format(sb, size, unit, omitSymbol);
113         return sb.toString();
114     }
115 
116     public void format(StringBuilder builder, long size) {
117         format(builder, size, null, false);
118     }
119 
120     public void format(StringBuilder builder, long size, ScaleUnit unit) {
121         format(builder, size, unit, false);
122     }
123 
124     private void format(StringBuilder builder, long size, ScaleUnit unit, boolean omitSymbol) {
125         if (size < 0L) {
126             throw new IllegalArgumentException("file size cannot be negative: " + size);
127         }
128         if (unit == null) {
129             unit = ScaleUnit.getScaleUnit(size);
130         }
131 
132         double scaledSize = (double) size / unit.bytes();
133 
134         if (unit == ScaleUnit.BYTE) {
135             builder.append(size);
136         } else if (scaledSize < 0.05d || scaledSize >= 10.0d) {
137             builder.append(Math.round(scaledSize));
138         } else {
139             builder.append(Math.round(scaledSize * 10d) / 10d);
140         }
141 
142         if (!omitSymbol) {
143             builder.append(" ").append(unit.symbol());
144         }
145     }
146 
147     public void format(MessageBuilder builder, long size) {
148         format(builder, size, null, false);
149     }
150 
151     public void format(MessageBuilder builder, long size, ScaleUnit unit) {
152         format(builder, size, unit, false);
153     }
154 
155     private void format(MessageBuilder builder, long size, ScaleUnit unit, boolean omitSymbol) {
156         if (size < 0L) {
157             throw new IllegalArgumentException("file size cannot be negative: " + size);
158         }
159         if (unit == null) {
160             unit = ScaleUnit.getScaleUnit(size);
161         }
162 
163         double scaledSize = (double) size / unit.bytes();
164 
165         if (unit == ScaleUnit.BYTE) {
166             builder.append(Long.toString(size));
167         } else if (scaledSize < 0.05d || scaledSize >= 10.0d) {
168             builder.append(Long.toString(Math.round(scaledSize)));
169         } else {
170             builder.append(Double.toString(Math.round(scaledSize * 10d) / 10d));
171         }
172 
173         if (!omitSymbol) {
174             builder.append(" ").append(unit.symbol());
175         }
176     }
177 
178     public String formatProgress(long progressedSize, long size) {
179         StringBuilder sb = new StringBuilder();
180         formatProgress(sb, progressedSize, size);
181         return sb.toString();
182     }
183 
184     public void formatProgress(StringBuilder builder, long progressedSize, long size) {
185         if (progressedSize < 0L) {
186             throw new IllegalArgumentException("progressed file size cannot be negative: " + size);
187         }
188         if (size >= 0 && progressedSize > size) {
189             throw new IllegalArgumentException(
190                     "progressed file size cannot be greater than size: " + progressedSize + " > " + size);
191         }
192 
193         if (size >= 0L && progressedSize != size) {
194             ScaleUnit unit = ScaleUnit.getScaleUnit(size);
195             format(builder, progressedSize, unit, true);
196             builder.append("/");
197             format(builder, size, unit, false);
198         } else {
199             ScaleUnit unit = ScaleUnit.getScaleUnit(progressedSize);
200 
201             format(builder, progressedSize, unit, false);
202         }
203     }
204 }