1 /* 2 * Copyright (C) 2008, Google Inc. 3 * Copyright (C) 2007-2010, Robin Rosenberg <robin.rosenberg@dewire.com> 4 * Copyright (C) 2008, Shawn O. Pearce <spearce@spearce.org> 5 * Copyright (C) 2009, Tor Arne Vestbø <torarnv@gmail.com> 6 * and other copyright owners as documented in the project's IP log. 7 * 8 * This program and the accompanying materials are made available 9 * under the terms of the Eclipse Distribution License v1.0 which 10 * accompanies this distribution, is reproduced below, and is 11 * available at http://www.eclipse.org/org/documents/edl-v10.php 12 * 13 * All rights reserved. 14 * 15 * Redistribution and use in source and binary forms, with or 16 * without modification, are permitted provided that the following 17 * conditions are met: 18 * 19 * - Redistributions of source code must retain the above copyright 20 * notice, this list of conditions and the following disclaimer. 21 * 22 * - Redistributions in binary form must reproduce the above 23 * copyright notice, this list of conditions and the following 24 * disclaimer in the documentation and/or other materials provided 25 * with the distribution. 26 * 27 * - Neither the name of the Eclipse Foundation, Inc. nor the 28 * names of its contributors may be used to endorse or promote 29 * products derived from this software without specific prior 30 * written permission. 31 * 32 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND 33 * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, 34 * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 35 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 36 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR 37 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 38 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 39 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 40 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 41 * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, 42 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 43 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF 44 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 45 */ 46 47 package org.eclipse.jgit.treewalk; 48 49 import java.io.ByteArrayInputStream; 50 import java.io.File; 51 import java.io.FileInputStream; 52 import java.io.IOException; 53 import java.io.InputStream; 54 55 import org.eclipse.jgit.errors.IncorrectObjectTypeException; 56 import org.eclipse.jgit.lib.Constants; 57 import org.eclipse.jgit.lib.FileMode; 58 import org.eclipse.jgit.lib.ObjectReader; 59 import org.eclipse.jgit.lib.Repository; 60 import org.eclipse.jgit.util.FS; 61 62 /** 63 * Working directory iterator for standard Java IO. 64 * <p> 65 * This iterator uses the standard <code>java.io</code> package to read the 66 * specified working directory as part of a {@link TreeWalk}. 67 */ 68 public class FileTreeIterator extends WorkingTreeIterator { 69 /** 70 * the starting directory. This directory should correspond to the root of 71 * the repository. 72 */ 73 protected final File directory; 74 75 /** 76 * the file system abstraction which will be necessary to perform certain 77 * file system operations. 78 */ 79 protected final FS fs; 80 81 /** 82 * Create a new iterator to traverse the work tree and its children. 83 * 84 * @param repo 85 * the repository whose working tree will be scanned. 86 */ 87 public FileTreeIterator(Repository repo) { 88 this(repo.getWorkTree(), repo.getFS(), 89 repo.getConfig().get(WorkingTreeOptions.KEY)); 90 initRootIterator(repo); 91 } 92 93 /** 94 * Create a new iterator to traverse the given directory and its children. 95 * 96 * @param root 97 * the starting directory. This directory should correspond to 98 * the root of the repository. 99 * @param fs 100 * the file system abstraction which will be necessary to perform 101 * certain file system operations. 102 * @param options 103 * working tree options to be used 104 */ 105 public FileTreeIterator(final File root, FS fs, WorkingTreeOptions options) { 106 super(options); 107 directory = root; 108 this.fs = fs; 109 init(entries()); 110 } 111 112 /** 113 * Create a new iterator to traverse a subdirectory. 114 * 115 * @param p 116 * the parent iterator we were created from. 117 * @param fs 118 * the file system abstraction which will be necessary to perform 119 * certain file system operations. 120 * @param root 121 * the subdirectory. This should be a directory contained within 122 * the parent directory. 123 */ 124 protected FileTreeIterator(final WorkingTreeIterator p, final File root, 125 FS fs) { 126 super(p); 127 directory = root; 128 this.fs = fs; 129 init(entries()); 130 } 131 132 @Override 133 public AbstractTreeIterator createSubtreeIterator(final ObjectReader reader) 134 throws IncorrectObjectTypeException, IOException { 135 return new FileTreeIterator(this, ((FileEntry) current()).getFile(), fs); 136 } 137 138 private Entry[] entries() { 139 final File[] all = directory.listFiles(); 140 if (all == null) 141 return EOF; 142 final Entry[] r = new Entry[all.length]; 143 for (int i = 0; i < r.length; i++) 144 r[i] = new FileEntry(all[i], fs); 145 return r; 146 } 147 148 /** 149 * Wrapper for a standard Java IO file 150 */ 151 static public class FileEntry extends Entry { 152 private final FileMode mode; 153 154 private FS.Attributes attributes; 155 156 private FS fs; 157 158 /** 159 * Create a new file entry. 160 * 161 * @param f 162 * file 163 * @param fs 164 * file system 165 */ 166 public FileEntry(File f, FS fs) { 167 this.fs = fs; 168 f = fs.normalize(f); 169 attributes = fs.getAttributes(f); 170 if (attributes.isSymbolicLink()) 171 mode = FileMode.SYMLINK; 172 else if (attributes.isDirectory()) { 173 if (new File(f, Constants.DOT_GIT).exists()) 174 mode = FileMode.GITLINK; 175 else 176 mode = FileMode.TREE; 177 } else if (attributes.isExecutable()) 178 mode = FileMode.EXECUTABLE_FILE; 179 else 180 mode = FileMode.REGULAR_FILE; 181 } 182 183 @Override 184 public FileMode getMode() { 185 return mode; 186 } 187 188 @Override 189 public String getName() { 190 return attributes.getName(); 191 } 192 193 @Override 194 public long getLength() { 195 return attributes.getLength(); 196 } 197 198 @Override 199 public long getLastModified() { 200 return attributes.getLastModifiedTime(); 201 } 202 203 @Override 204 public InputStream openInputStream() throws IOException { 205 if (fs.isSymLink(getFile())) 206 return new ByteArrayInputStream(fs.readSymLink(getFile()) 207 .getBytes( 208 Constants.CHARACTER_ENCODING)); 209 else 210 return new FileInputStream(getFile()); 211 } 212 213 /** 214 * Get the underlying file of this entry. 215 * 216 * @return the underlying file of this entry 217 */ 218 public File getFile() { 219 return attributes.getFile(); 220 } 221 } 222 223 /** 224 * @return The root directory of this iterator 225 */ 226 public File getDirectory() { 227 return directory; 228 } 229 230 /** 231 * @return The location of the working file. This is the same as {@code new 232 * File(getDirectory(), getEntryPath())} but may be faster by 233 * reusing an internal File instance. 234 */ 235 public File getEntryFile() { 236 return ((FileEntry) current()).getFile(); 237 } 238 239 @Override 240 protected byte[] idSubmodule(final Entry e) { 241 if (repository == null) 242 return idSubmodule(getDirectory(), e); 243 return super.idSubmodule(e); 244 } 245 }