Refactor AbstractTreeIterator semantics to start on first entry
[egit.git] / org.spearce.egit.core / src / org / spearce / egit / core / ContainerTreeIterator.java
blob2b7ff3b93eada2a7ef2fd3de85157cd5fce6a274
1 /*******************************************************************************
2 * Copyright (C) 2008, Google Inc.
4 * All rights reserved. This program and the accompanying materials
5 * are made available under the terms of the Eclipse Public License v1.0
6 * See LICENSE for the full license text, also available.
7 *******************************************************************************/
9 package org.spearce.egit.core;
11 import java.io.File;
12 import java.io.IOException;
13 import java.io.InputStream;
15 import org.eclipse.core.resources.IContainer;
16 import org.eclipse.core.resources.IFile;
17 import org.eclipse.core.resources.IResource;
18 import org.eclipse.core.runtime.CoreException;
19 import org.spearce.egit.core.project.RepositoryMapping;
20 import org.spearce.jgit.errors.IncorrectObjectTypeException;
21 import org.spearce.jgit.lib.Constants;
22 import org.spearce.jgit.lib.FileMode;
23 import org.spearce.jgit.lib.ObjectId;
24 import org.spearce.jgit.lib.Repository;
25 import org.spearce.jgit.treewalk.AbstractTreeIterator;
26 import org.spearce.jgit.treewalk.WorkingTreeIterator;
27 import org.spearce.jgit.util.FS;
29 /**
30 * Adapts an Eclipse {@link IContainer} for use in a <code>TreeWalk</code>.
31 * <p>
32 * This iterator converts an Eclipse IContainer object into something that a
33 * TreeWalk instance can iterate over in parallel with any other Git tree data
34 * structure, such as another working directory tree from outside of the
35 * workspace or a stored tree from a Repository object database.
36 * <p>
37 * Modification times provided by this iterator are obtained from the cache
38 * Eclipse uses to track external resource modification. This can be faster, but
39 * requires the user refresh their workspace when external modifications take
40 * place. This is not really a concern as it is common practice to need to do a
41 * workspace refresh after externally modifying a file.
43 * @see org.spearce.jgit.treewalk.TreeWalk
45 public class ContainerTreeIterator extends WorkingTreeIterator {
46 private static String computePrefix(final IContainer base) {
47 final RepositoryMapping rm = RepositoryMapping.getMapping(base);
48 if (rm == null)
49 throw new IllegalArgumentException("Not in a Git project: " + base);
50 return rm.getRepoRelativePath(base);
53 private final IContainer node;
55 /**
56 * Construct a new iterator from the workspace.
57 * <p>
58 * The iterator will support traversal over the named container, but only if
59 * it is contained within a project which has the Git repository provider
60 * connected and this resource is mapped into a Git repository. During the
61 * iteration the paths will be automatically generated to match the proper
62 * repository paths for this container's children.
64 * @param base
65 * the part of the workspace the iterator will walk over.
67 public ContainerTreeIterator(final IContainer base) {
68 super(computePrefix(base));
69 node = base;
70 init(entries());
73 private ContainerTreeIterator(final WorkingTreeIterator p,
74 final IContainer base) {
75 super(p);
76 node = base;
77 init(entries());
80 @Override
81 public AbstractTreeIterator createSubtreeIterator(final Repository db)
82 throws IncorrectObjectTypeException, IOException {
83 if (FileMode.TREE.equals(mode))
84 return new ContainerTreeIterator(this,
85 (IContainer) ((ResourceEntry) current()).rsrc);
86 else
87 throw new IncorrectObjectTypeException(ObjectId.zeroId(),
88 Constants.TYPE_TREE);
91 private Entry[] entries() {
92 final IResource[] all;
93 try {
94 // all = node.members(IContainer.INCLUDE_HIDDEN); 3.4 flag
95 all = node.members(0);
96 } catch (CoreException err) {
97 return EOF;
100 final Entry[] r = new Entry[all.length];
101 for (int i = 0; i < r.length; i++)
102 r[i] = new ResourceEntry(all[i]);
103 return r;
106 static class ResourceEntry extends Entry {
107 final IResource rsrc;
109 private final FileMode mode;
111 private long length = -1;
113 ResourceEntry(final IResource f) {
114 rsrc = f;
116 switch (f.getType()) {
117 case IResource.FILE:
118 if (FS.INSTANCE.canExecute(asFile()))
119 mode = FileMode.EXECUTABLE_FILE;
120 else
121 mode = FileMode.REGULAR_FILE;
122 break;
123 case IResource.FOLDER: {
124 final IContainer c = (IContainer) f;
125 if (c.findMember(".git") != null)
126 mode = FileMode.GITLINK;
127 else
128 mode = FileMode.TREE;
129 break;
131 default:
132 mode = FileMode.MISSING;
133 break;
137 @Override
138 public FileMode getMode() {
139 return mode;
142 @Override
143 public String getName() {
144 return rsrc.getName();
147 @Override
148 public long getLength() {
149 if (length < 0) {
150 if (rsrc instanceof IFile)
151 length = asFile().length();
152 else
153 length = 0;
155 return length;
158 @Override
159 public long getLastModified() {
160 return rsrc.getLocalTimeStamp();
163 @Override
164 public InputStream openInputStream() throws IOException {
165 if (rsrc instanceof IFile) {
166 try {
167 return ((IFile) rsrc).getContents(true);
168 } catch (CoreException err) {
169 final IOException ioe = new IOException(err.getMessage());
170 ioe.initCause(err);
171 throw ioe;
174 throw new IOException("Not a regular file: " + rsrc);
177 private File asFile() {
178 return ((IFile) rsrc).getLocation().toFile();