View Javadoc
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 }