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.hdfs.protocol;
019
020import java.util.Arrays;
021import java.util.Collections;
022import java.util.List;
023
024import org.apache.hadoop.fs.Path;
025import org.apache.hadoop.hdfs.DFSUtil;
026import org.apache.hadoop.hdfs.server.namenode.snapshot.INodeDirectorySnapshottable.SnapshotDiffInfo;
027
028/**
029 * This class represents to end users the difference between two snapshots of 
030 * the same directory, or the difference between a snapshot of the directory and
031 * its current state. Instead of capturing all the details of the diff, which 
032 * is stored in {@link SnapshotDiffInfo}, this class only lists where the 
033 * changes happened and their types.
034 */
035public class SnapshotDiffReport {
036  private final static String LINE_SEPARATOR = System.getProperty(
037      "line.separator", "\n");
038
039  /**
040   * Types of the difference, which include CREATE, MODIFY, DELETE, and RENAME.
041   * Each type has a label for representation: +/M/-/R represent CREATE, MODIFY,
042   * DELETE, and RENAME respectively.
043   */
044  public enum DiffType {
045    CREATE("+"),     
046    MODIFY("M"),    
047    DELETE("-"), 
048    RENAME("R");
049    
050    private String label;
051    
052    private DiffType(String label) {
053      this.label = label;
054    }
055    
056    public String getLabel() {
057      return label;
058    }
059    
060    public static DiffType getTypeFromLabel(String label) {
061      if (label.equals(CREATE.getLabel())) {
062        return CREATE;
063      } else if (label.equals(MODIFY.getLabel())) {
064        return MODIFY;
065      } else if (label.equals(DELETE.getLabel())) {
066        return DELETE;
067      } else if (label.equals(RENAME.getLabel())) {
068        return RENAME;
069      }
070      return null;
071    }
072  };
073  
074  /**
075   * Representing the full path and diff type of a file/directory where changes
076   * have happened.
077   */
078  public static class DiffReportEntry {
079    /** The type of the difference. */
080    private final DiffType type;
081    /**
082     * The relative path (related to the snapshot root) of the file/directory
083     * where changes have happened
084     */
085    private final byte[] relativePath;
086
087    public DiffReportEntry(DiffType type, byte[] path) {
088      this.type = type;
089      this.relativePath = path;
090    }
091    
092    public DiffReportEntry(DiffType type, byte[][] pathComponents) {
093      this.type = type;
094      this.relativePath = DFSUtil.byteArray2bytes(pathComponents);
095    }
096    
097    @Override
098    public String toString() {
099      return type.getLabel() + "\t" + getRelativePathString();
100    }
101    
102    public DiffType getType() {
103      return type;
104    }
105
106    public String getRelativePathString() {
107      String path = DFSUtil.bytes2String(relativePath);
108      if (path.isEmpty()) {
109        return Path.CUR_DIR;
110      } else {
111        return Path.CUR_DIR + Path.SEPARATOR + path;
112      }
113    }
114
115    public byte[] getRelativePath() {
116      return relativePath;
117    }
118    
119    @Override
120    public boolean equals(Object other) {
121      if (this == other) {
122        return true;
123      } 
124      if (other != null && other instanceof DiffReportEntry) {
125        DiffReportEntry entry = (DiffReportEntry) other;
126        return type.equals(entry.getType())
127            && Arrays.equals(relativePath, entry.getRelativePath());
128      }
129      return false;
130    }
131    
132    @Override
133    public int hashCode() {
134      return Arrays.hashCode(relativePath);
135    }
136  }
137  
138  /** snapshot root full path */
139  private final String snapshotRoot;
140
141  /** start point of the diff */
142  private final String fromSnapshot;
143  
144  /** end point of the diff */
145  private final String toSnapshot;
146  
147  /** list of diff */
148  private final List<DiffReportEntry> diffList;
149  
150  public SnapshotDiffReport(String snapshotRoot, String fromSnapshot,
151      String toSnapshot, List<DiffReportEntry> entryList) {
152    this.snapshotRoot = snapshotRoot;
153    this.fromSnapshot = fromSnapshot;
154    this.toSnapshot = toSnapshot;
155    this.diffList = entryList != null ? entryList : Collections
156        .<DiffReportEntry> emptyList();
157  }
158  
159  /** @return {@link #snapshotRoot}*/
160  public String getSnapshotRoot() {
161    return snapshotRoot;
162  }
163
164  /** @return {@link #fromSnapshot} */
165  public String getFromSnapshot() {
166    return fromSnapshot;
167  }
168
169  /** @return {@link #toSnapshot} */
170  public String getLaterSnapshotName() {
171    return toSnapshot;
172  }
173  
174  /** @return {@link #diffList} */
175  public List<DiffReportEntry> getDiffList() {
176    return diffList;
177  }
178  
179  @Override
180  public String toString() {
181    StringBuilder str = new StringBuilder();
182    String from = fromSnapshot == null || fromSnapshot.isEmpty() ? 
183        "current directory" : "snapshot " + fromSnapshot;
184    String to = toSnapshot == null || toSnapshot.isEmpty() ? "current directory"
185        : "snapshot " + toSnapshot;
186    str.append("Difference between " + from + " and " + to
187        + " under directory " + snapshotRoot + ":" + LINE_SEPARATOR);
188    for (DiffReportEntry entry : diffList) {
189      str.append(entry.toString() + LINE_SEPARATOR);
190    }
191    return str.toString();
192  }
193}