Use Set instead of array to keep track of change listeners
[egit/torarne.git] / org.spearce.egit.core / src / org / spearce / egit / core / ContainerTreeIterator.java
blob6d6b72e144b2b4b00399c3e7627a585a6fed635a
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.resources.IWorkspaceRoot;
19 import org.eclipse.core.runtime.CoreException;
20 import org.spearce.egit.core.project.RepositoryMapping;
21 import org.spearce.jgit.errors.IncorrectObjectTypeException;
22 import org.spearce.jgit.lib.Constants;
23 import org.spearce.jgit.lib.FileMode;
24 import org.spearce.jgit.lib.ObjectId;
25 import org.spearce.jgit.lib.Repository;
26 import org.spearce.jgit.treewalk.AbstractTreeIterator;
27 import org.spearce.jgit.treewalk.WorkingTreeIterator;
28 import org.spearce.jgit.util.FS;
30 /**
31 * Adapts an Eclipse {@link IContainer} for use in a <code>TreeWalk</code>.
32 * <p>
33 * This iterator converts an Eclipse IContainer object into something that a
34 * TreeWalk instance can iterate over in parallel with any other Git tree data
35 * structure, such as another working directory tree from outside of the
36 * workspace or a stored tree from a Repository object database.
37 * <p>
38 * Modification times provided by this iterator are obtained from the cache
39 * Eclipse uses to track external resource modification. This can be faster, but
40 * requires the user refresh their workspace when external modifications take
41 * place. This is not really a concern as it is common practice to need to do a
42 * workspace refresh after externally modifying a file.
44 * @see org.spearce.jgit.treewalk.TreeWalk
46 public class ContainerTreeIterator extends WorkingTreeIterator {
47 private static String computePrefix(final IContainer base) {
48 final RepositoryMapping rm = RepositoryMapping.getMapping(base);
49 if (rm == null)
50 throw new IllegalArgumentException("Not in a Git project: " + base);
51 return rm.getRepoRelativePath(base);
54 private final IContainer node;
56 /**
57 * Construct a new iterator from a container in the workspace.
58 * <p>
59 * The iterator will support traversal over the named container, but only if
60 * it is contained within a project which has the Git repository provider
61 * connected and this resource is mapped into a Git repository. During the
62 * iteration the paths will be automatically generated to match the proper
63 * repository paths for this container's children.
65 * @param base
66 * the part of the workspace the iterator will walk over.
68 public ContainerTreeIterator(final IContainer base) {
69 super(computePrefix(base));
70 node = base;
71 init(entries());
74 /**
75 * Construct a new iterator from the workspace root.
76 * <p>
77 * The iterator will support traversal over workspace projects that have
78 * a Git repository provider connected and is mapped into a Git repository.
79 * During the iteration the paths will be automatically generated to match
80 * the proper repository paths for this container's children.
82 * @param root
83 * the workspace root to walk over.
85 public ContainerTreeIterator(final IWorkspaceRoot root) {
86 super("");
87 node = root;
88 init(entries());
91 private ContainerTreeIterator(final WorkingTreeIterator p,
92 final IContainer base) {
93 super(p);
94 node = base;
95 init(entries());
98 @Override
99 public AbstractTreeIterator createSubtreeIterator(final Repository db)
100 throws IncorrectObjectTypeException, IOException {
101 if (FileMode.TREE.equals(mode))
102 return new ContainerTreeIterator(this,
103 (IContainer) ((ResourceEntry) current()).rsrc);
104 else
105 throw new IncorrectObjectTypeException(ObjectId.zeroId(),
106 Constants.TYPE_TREE);
109 private Entry[] entries() {
110 final IResource[] all;
111 try {
112 all = node.members(IContainer.INCLUDE_HIDDEN);
113 } catch (CoreException err) {
114 return EOF;
117 final Entry[] r = new Entry[all.length];
118 for (int i = 0; i < r.length; i++)
119 r[i] = new ResourceEntry(all[i]);
120 return r;
123 static class ResourceEntry extends Entry {
124 final IResource rsrc;
126 private final FileMode mode;
128 private long length = -1;
130 ResourceEntry(final IResource f) {
131 rsrc = f;
133 switch (f.getType()) {
134 case IResource.FILE:
135 if (FS.INSTANCE.canExecute(asFile()))
136 mode = FileMode.EXECUTABLE_FILE;
137 else
138 mode = FileMode.REGULAR_FILE;
139 break;
140 case IResource.PROJECT:
141 case IResource.FOLDER: {
142 final IContainer c = (IContainer) f;
143 if (c.findMember(".git") != null)
144 mode = FileMode.GITLINK;
145 else
146 mode = FileMode.TREE;
147 break;
149 default:
150 mode = FileMode.MISSING;
151 break;
155 @Override
156 public FileMode getMode() {
157 return mode;
160 @Override
161 public String getName() {
162 if (rsrc.getType() == IResource.PROJECT)
163 return rsrc.getLocation().lastSegment();
164 else
165 return rsrc.getName();
168 @Override
169 public long getLength() {
170 if (length < 0) {
171 if (rsrc instanceof IFile)
172 length = asFile().length();
173 else
174 length = 0;
176 return length;
179 @Override
180 public long getLastModified() {
181 return rsrc.getLocalTimeStamp();
184 @Override
185 public InputStream openInputStream() throws IOException {
186 if (rsrc instanceof IFile) {
187 try {
188 return ((IFile) rsrc).getContents(true);
189 } catch (CoreException err) {
190 final IOException ioe = new IOException(err.getMessage());
191 ioe.initCause(err);
192 throw ioe;
195 throw new IOException("Not a regular file: " + rsrc);
198 private File asFile() {
199 return ((IFile) rsrc).getLocation().toFile();