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     */
018    package org.apache.hadoop.hdfs.protocol;
019    
020    import java.util.Arrays;
021    import java.util.Collections;
022    import java.util.List;
023    
024    import org.apache.hadoop.fs.Path;
025    import org.apache.hadoop.hdfs.DFSUtil;
026    import 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     */
035    public 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    }