001/**
002 * Licensed to the Apache Software Foundation (ASF) under one
003 * or more contributor license agreements.  See the NOTICE file
004 * distributed with this work for additional information
005 * regarding copyright ownership.  The ASF licenses this file
006 * to you under the Apache License, Version 2.0 (the
007 * "License"); you may not use this file except in compliance
008 * with the License.  You may obtain a copy of the License at
009 *
010 *     http://www.apache.org/licenses/LICENSE-2.0
011 *
012 * Unless required by applicable law or agreed to in writing, software
013 * distributed under the License is distributed on an "AS IS" BASIS,
014 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
015 * See the License for the specific language governing permissions and
016 * limitations under the License.
017 */
018package org.apache.hadoop.fs;
019
020import java.io.DataInput;
021import java.io.DataOutput;
022import java.io.IOException;
023import java.util.List;
024
025import org.apache.hadoop.classification.InterfaceAudience;
026import org.apache.hadoop.classification.InterfaceStability;
027import org.apache.hadoop.io.Writable;
028import org.apache.hadoop.util.StringUtils;
029
030/** Store the summary of a content (a directory or a file). */
031@InterfaceAudience.Public
032@InterfaceStability.Evolving
033public class ContentSummary implements Writable{
034  private long length;
035  private long fileCount;
036  private long directoryCount;
037  private long quota;
038  private long spaceConsumed;
039  private long spaceQuota;
040  private long typeConsumed[];
041  private long typeQuota[];
042
043  public static class Builder{
044    public Builder() {
045      this.quota = -1;
046      this.spaceQuota = -1;
047
048      typeConsumed = new long[StorageType.values().length];
049      typeQuota = new long[StorageType.values().length];
050      for (int i = 0; i < typeQuota.length; i++) {
051        typeQuota[i] = -1;
052      }
053    }
054
055    public Builder length(long length) {
056      this.length = length;
057      return this;
058    }
059
060    public Builder fileCount(long fileCount) {
061      this.fileCount = fileCount;
062      return this;
063    }
064
065    public Builder directoryCount(long directoryCount) {
066      this.directoryCount = directoryCount;
067      return this;
068    }
069
070    public Builder quota(long quota){
071      this.quota = quota;
072      return this;
073    }
074
075    public Builder spaceConsumed(long spaceConsumed) {
076      this.spaceConsumed = spaceConsumed;
077      return this;
078    }
079
080    public Builder spaceQuota(long spaceQuota) {
081      this.spaceQuota = spaceQuota;
082      return this;
083    }
084
085    public Builder typeConsumed(long typeConsumed[]) {
086      for (int i = 0; i < typeConsumed.length; i++) {
087        this.typeConsumed[i] = typeConsumed[i];
088      }
089      return this;
090    }
091
092    public Builder typeQuota(StorageType type, long quota) {
093      this.typeQuota[type.ordinal()] = quota;
094      return this;
095    }
096
097    public Builder typeConsumed(StorageType type, long consumed) {
098      this.typeConsumed[type.ordinal()] = consumed;
099      return this;
100    }
101
102    public Builder typeQuota(long typeQuota[]) {
103      for (int i = 0; i < typeQuota.length; i++) {
104        this.typeQuota[i] = typeQuota[i];
105      }
106      return this;
107    }
108
109    public ContentSummary build() {
110      return new ContentSummary(length, fileCount, directoryCount, quota,
111          spaceConsumed, spaceQuota, typeConsumed, typeQuota);
112    }
113
114    private long length;
115    private long fileCount;
116    private long directoryCount;
117    private long quota;
118    private long spaceConsumed;
119    private long spaceQuota;
120    private long typeConsumed[];
121    private long typeQuota[];
122  }
123
124  /** Constructor deprecated by ContentSummary.Builder*/
125  @Deprecated
126  public ContentSummary() {}
127  
128  /** Constructor, deprecated by ContentSummary.Builder
129   *  This constructor implicitly set spaceConsumed the same as length.
130   *  spaceConsumed and length must be set explicitly with
131   *  ContentSummary.Builder
132   * */
133  @Deprecated
134  public ContentSummary(long length, long fileCount, long directoryCount) {
135    this(length, fileCount, directoryCount, -1L, length, -1L);
136  }
137
138  /** Constructor, deprecated by ContentSummary.Builder */
139  @Deprecated
140  public ContentSummary(
141      long length, long fileCount, long directoryCount, long quota,
142      long spaceConsumed, long spaceQuota) {
143    this.length = length;
144    this.fileCount = fileCount;
145    this.directoryCount = directoryCount;
146    this.quota = quota;
147    this.spaceConsumed = spaceConsumed;
148    this.spaceQuota = spaceQuota;
149  }
150
151  /** Constructor for ContentSummary.Builder*/
152  private ContentSummary(
153      long length, long fileCount, long directoryCount, long quota,
154      long spaceConsumed, long spaceQuota, long typeConsumed[],
155      long typeQuota[]) {
156    this.length = length;
157    this.fileCount = fileCount;
158    this.directoryCount = directoryCount;
159    this.quota = quota;
160    this.spaceConsumed = spaceConsumed;
161    this.spaceQuota = spaceQuota;
162    this.typeConsumed = typeConsumed;
163    this.typeQuota = typeQuota;
164  }
165
166  /** @return the length */
167  public long getLength() {return length;}
168
169  /** @return the directory count */
170  public long getDirectoryCount() {return directoryCount;}
171
172  /** @return the file count */
173  public long getFileCount() {return fileCount;}
174  
175  /** Return the directory quota */
176  public long getQuota() {return quota;}
177  
178  /** Returns storage space consumed */
179  public long getSpaceConsumed() {return spaceConsumed;}
180
181  /** Returns storage space quota */
182  public long getSpaceQuota() {return spaceQuota;}
183
184  /** Returns storage type quota */
185  public long getTypeQuota(StorageType type) {
186    return (typeQuota != null) ? typeQuota[type.ordinal()] : -1;
187  }
188
189  /** Returns storage type consumed*/
190  public long getTypeConsumed(StorageType type) {
191    return (typeConsumed != null) ? typeConsumed[type.ordinal()] : 0;
192  }
193
194  /** Returns true if any storage type quota has been set*/
195  public boolean isTypeQuotaSet() {
196    if (typeQuota == null) {
197      return false;
198    }
199    for (StorageType t : StorageType.getTypesSupportingQuota()) {
200      if (typeQuota[t.ordinal()] > 0) {
201        return true;
202      }
203    }
204    return false;
205  }
206
207  /** Returns true if any storage type consumption information is available*/
208  public boolean isTypeConsumedAvailable() {
209    if (typeConsumed == null) {
210      return false;
211    }
212    for (StorageType t : StorageType.getTypesSupportingQuota()) {
213      if (typeConsumed[t.ordinal()] > 0) {
214        return true;
215      }
216    }
217    return false;
218  }
219
220  @Override
221  @InterfaceAudience.Private
222  public void write(DataOutput out) throws IOException {
223    out.writeLong(length);
224    out.writeLong(fileCount);
225    out.writeLong(directoryCount);
226    out.writeLong(quota);
227    out.writeLong(spaceConsumed);
228    out.writeLong(spaceQuota);
229  }
230
231  @Override
232  @InterfaceAudience.Private
233  public void readFields(DataInput in) throws IOException {
234    this.length = in.readLong();
235    this.fileCount = in.readLong();
236    this.directoryCount = in.readLong();
237    this.quota = in.readLong();
238    this.spaceConsumed = in.readLong();
239    this.spaceQuota = in.readLong();
240  }
241
242  /**
243   * Output format:
244   * <----12----> <----12----> <-------18------->
245   *    DIR_COUNT   FILE_COUNT       CONTENT_SIZE
246   */
247  private static final String SUMMARY_FORMAT = "%12s %12s %18s ";
248  /**
249   * Output format:
250   * <----12----> <------15-----> <------15-----> <------15----->
251   *        QUOTA       REM_QUOTA     SPACE_QUOTA REM_SPACE_QUOTA
252   * <----12----> <----12----> <-------18------->
253   *    DIR_COUNT   FILE_COUNT       CONTENT_SIZE
254   */
255  private static final String QUOTA_SUMMARY_FORMAT = "%12s %15s ";
256  private static final String SPACE_QUOTA_SUMMARY_FORMAT = "%15s %15s ";
257
258  private static final String STORAGE_TYPE_SUMMARY_FORMAT = "%13s %17s ";
259
260  private static final String[] HEADER_FIELDS = new String[] { "DIR_COUNT",
261      "FILE_COUNT", "CONTENT_SIZE"};
262  private static final String[] QUOTA_HEADER_FIELDS = new String[] { "QUOTA",
263      "REM_QUOTA", "SPACE_QUOTA", "REM_SPACE_QUOTA" };
264
265  /** The header string */
266  private static final String HEADER = String.format(
267      SUMMARY_FORMAT, (Object[]) HEADER_FIELDS);
268
269  private static final String QUOTA_HEADER = String.format(
270      QUOTA_SUMMARY_FORMAT + SPACE_QUOTA_SUMMARY_FORMAT,
271      (Object[]) QUOTA_HEADER_FIELDS) +
272      HEADER;
273
274  /** default quota display string */
275  private static final String QUOTA_NONE = "none";
276  private static final String QUOTA_INF = "inf";
277
278  /** Return the header of the output.
279   * if qOption is false, output directory count, file count, and content size;
280   * if qOption is true, output quota and remaining quota as well.
281   * 
282   * @param qOption a flag indicating if quota needs to be printed or not
283   * @return the header of the output
284   */
285  public static String getHeader(boolean qOption) {
286    return qOption ? QUOTA_HEADER : HEADER;
287  }
288
289  /**
290   * return the header of with the StorageTypes
291   *
292   * @param storageTypes
293   * @return storage header string
294   */
295  public static String getStorageTypeHeader(List<StorageType> storageTypes) {
296    StringBuffer header = new StringBuffer();
297
298    for (StorageType st : storageTypes) {
299      /* the field length is 13/17 for quota and remain quota
300       * as the max length for quota name is ARCHIVE_QUOTA
301        * and remain quota name REM_ARCHIVE_QUOTA */
302      String storageName = st.toString();
303      header.append(String.format(STORAGE_TYPE_SUMMARY_FORMAT, storageName + "_QUOTA",
304          "REM_" + storageName + "_QUOTA"));
305    }
306    return header.toString();
307  }
308
309  /**
310   * Returns the names of the fields from the summary header.
311   * 
312   * @return names of fields as displayed in the header
313   */
314  public static String[] getHeaderFields() {
315    return HEADER_FIELDS;
316  }
317
318  /**
319   * Returns the names of the fields used in the quota summary.
320   * 
321   * @return names of quota fields as displayed in the header
322   */
323  public static String[] getQuotaHeaderFields() {
324    return QUOTA_HEADER_FIELDS;
325  }
326
327  @Override
328  public String toString() {
329    return toString(true);
330  }
331
332  /** Return the string representation of the object in the output format.
333   * if qOption is false, output directory count, file count, and content size;
334   * if qOption is true, output quota and remaining quota as well.
335   *
336   * @param qOption a flag indicating if quota needs to be printed or not
337   * @return the string representation of the object
338  */
339  public String toString(boolean qOption) {
340    return toString(qOption, false);
341  }
342
343  /** Return the string representation of the object in the output format.
344   * if qOption is false, output directory count, file count, and content size;
345   * if qOption is true, output quota and remaining quota as well.
346   * if hOption is false file sizes are returned in bytes
347   * if hOption is true file sizes are returned in human readable 
348   * 
349   * @param qOption a flag indicating if quota needs to be printed or not
350   * @param hOption a flag indicating if human readable output if to be used
351   * @return the string representation of the object
352   */
353  public String toString(boolean qOption, boolean hOption) {
354    return toString(qOption, hOption, false, null);
355  }
356
357  /**
358   * Return the string representation of the object in the output format.
359   * if tOption is true, display the quota by storage types,
360   * Otherwise, same logic with #toString(boolean,boolean)
361   *
362   * @param qOption a flag indicating if quota needs to be printed or not
363   * @param hOption a flag indicating if human readable output if to be used
364   * @param tOption a flag indicating if display quota by storage types
365   * @param types Storage types to display
366   * @return the string representation of the object
367   */
368  public String toString(boolean qOption, boolean hOption,
369                         boolean tOption, List<StorageType> types) {
370    String prefix = "";
371
372    if (tOption) {
373      StringBuffer content = new StringBuffer();
374      for (StorageType st : types) {
375        long typeQuota = getTypeQuota(st);
376        long typeConsumed = getTypeConsumed(st);
377        String quotaStr = QUOTA_NONE;
378        String quotaRem = QUOTA_INF;
379
380        if (typeQuota > 0) {
381          quotaStr = formatSize(typeQuota, hOption);
382          quotaRem = formatSize(typeQuota - typeConsumed, hOption);
383        }
384
385        content.append(String.format(STORAGE_TYPE_SUMMARY_FORMAT,
386            quotaStr, quotaRem));
387      }
388      return content.toString();
389    }
390
391    if (qOption) {
392      String quotaStr = QUOTA_NONE;
393      String quotaRem = QUOTA_INF;
394      String spaceQuotaStr = QUOTA_NONE;
395      String spaceQuotaRem = QUOTA_INF;
396
397      if (quota>0) {
398        quotaStr = formatSize(quota, hOption);
399        quotaRem = formatSize(quota-(directoryCount+fileCount), hOption);
400      }
401      if (spaceQuota>0) {
402        spaceQuotaStr = formatSize(spaceQuota, hOption);
403        spaceQuotaRem = formatSize(spaceQuota - spaceConsumed, hOption);
404      }
405
406      prefix = String.format(QUOTA_SUMMARY_FORMAT + SPACE_QUOTA_SUMMARY_FORMAT,
407          quotaStr, quotaRem, spaceQuotaStr, spaceQuotaRem);
408    }
409
410    return prefix + String.format(SUMMARY_FORMAT,
411        formatSize(directoryCount, hOption),
412        formatSize(fileCount, hOption),
413        formatSize(length, hOption));
414  }
415
416  /**
417   * Formats a size to be human readable or in bytes
418   * @param size value to be formatted
419   * @param humanReadable flag indicating human readable or not
420   * @return String representation of the size
421  */
422  private String formatSize(long size, boolean humanReadable) {
423    return humanReadable
424      ? StringUtils.TraditionalBinaryPrefix.long2String(size, "", 1)
425      : String.valueOf(size);
426  }
427}