~ubuntu-branches/ubuntu/oneiric/monodevelop/oneiric

« back to all changes in this revision

Viewing changes to contrib/NGit/NGit.Ignore/IgnoreRule.cs

  • Committer: Bazaar Package Importer
  • Author(s): Jo Shields
  • Date: 2011-06-27 17:03:13 UTC
  • mto: (1.8.1 upstream)
  • mto: This revision was merged to the branch mainline in revision 54.
  • Revision ID: james.westby@ubuntu.com-20110627170313-6cvz3s19x6e9hqe9
ImportĀ upstreamĀ versionĀ 2.5.92+dfsg

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*
 
2
This code is derived from jgit (http://eclipse.org/jgit).
 
3
Copyright owners are documented in jgit's IP log.
 
4
 
 
5
This program and the accompanying materials are made available
 
6
under the terms of the Eclipse Distribution License v1.0 which
 
7
accompanies this distribution, is reproduced below, and is
 
8
available at http://www.eclipse.org/org/documents/edl-v10.php
 
9
 
 
10
All rights reserved.
 
11
 
 
12
Redistribution and use in source and binary forms, with or
 
13
without modification, are permitted provided that the following
 
14
conditions are met:
 
15
 
 
16
- Redistributions of source code must retain the above copyright
 
17
  notice, this list of conditions and the following disclaimer.
 
18
 
 
19
- Redistributions in binary form must reproduce the above
 
20
  copyright notice, this list of conditions and the following
 
21
  disclaimer in the documentation and/or other materials provided
 
22
  with the distribution.
 
23
 
 
24
- Neither the name of the Eclipse Foundation, Inc. nor the
 
25
  names of its contributors may be used to endorse or promote
 
26
  products derived from this software without specific prior
 
27
  written permission.
 
28
 
 
29
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
 
30
CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
 
31
INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 
32
OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 
33
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
 
34
CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 
35
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
 
36
NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 
37
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
 
38
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
 
39
STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 
40
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
 
41
ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 
42
*/
 
43
 
 
44
using NGit.Errors;
 
45
using NGit.Fnmatch;
 
46
using Sharpen;
 
47
 
 
48
namespace NGit.Ignore
 
49
{
 
50
        /// <summary>
 
51
        /// A single ignore rule corresponding to one line in a .gitignore or
 
52
        /// ignore file.
 
53
        /// </summary>
 
54
        /// <remarks>
 
55
        /// A single ignore rule corresponding to one line in a .gitignore or
 
56
        /// ignore file. Parses the ignore pattern
 
57
        /// Inspiration from: Ferry Huberts
 
58
        /// </remarks>
 
59
        public class IgnoreRule
 
60
        {
 
61
                private string pattern;
 
62
 
 
63
                private bool negation;
 
64
 
 
65
                private bool nameOnly;
 
66
 
 
67
                private bool dirOnly;
 
68
 
 
69
                private FileNameMatcher matcher;
 
70
 
 
71
                /// <summary>Create a new ignore rule with the given pattern.</summary>
 
72
                /// <remarks>
 
73
                /// Create a new ignore rule with the given pattern. Assumes that
 
74
                /// the pattern is already trimmed.
 
75
                /// </remarks>
 
76
                /// <param name="pattern">
 
77
                /// Base pattern for the ignore rule. This pattern will
 
78
                /// be parsed to generate rule parameters.
 
79
                /// </param>
 
80
                public IgnoreRule(string pattern)
 
81
                {
 
82
                        this.pattern = pattern;
 
83
                        negation = false;
 
84
                        nameOnly = false;
 
85
                        dirOnly = false;
 
86
                        matcher = null;
 
87
                        Setup();
 
88
                }
 
89
 
 
90
                /// <summary>Remove leading/trailing characters as needed.</summary>
 
91
                /// <remarks>
 
92
                /// Remove leading/trailing characters as needed. Set up
 
93
                /// rule variables for later matching.
 
94
                /// </remarks>
 
95
                private void Setup()
 
96
                {
 
97
                        int startIndex = 0;
 
98
                        int endIndex = pattern.Length;
 
99
                        if (pattern.StartsWith("!"))
 
100
                        {
 
101
                                startIndex++;
 
102
                                negation = true;
 
103
                        }
 
104
                        if (pattern.EndsWith("/"))
 
105
                        {
 
106
                                endIndex--;
 
107
                                dirOnly = true;
 
108
                        }
 
109
                        pattern = Sharpen.Runtime.Substring(pattern, startIndex, endIndex);
 
110
                        bool hasSlash = pattern.Contains("/");
 
111
                        if (!hasSlash)
 
112
                        {
 
113
                                nameOnly = true;
 
114
                        }
 
115
                        else
 
116
                        {
 
117
                                if (!pattern.StartsWith("/"))
 
118
                                {
 
119
                                        //Contains "/" but does not start with one
 
120
                                        //Adding / to the start should not interfere with matching
 
121
                                        pattern = "/" + pattern;
 
122
                                }
 
123
                        }
 
124
                        if (pattern.Contains("*") || pattern.Contains("?") || pattern.Contains("["))
 
125
                        {
 
126
                                try
 
127
                                {
 
128
                                        matcher = new FileNameMatcher(pattern, '/');
 
129
                                }
 
130
                                catch (InvalidPatternException e)
 
131
                                {
 
132
                                        Sharpen.Runtime.PrintStackTrace(e);
 
133
                                }
 
134
                        }
 
135
                }
 
136
 
 
137
                /// <returns>True if the pattern is just a file name and not a path</returns>
 
138
                public virtual bool GetNameOnly()
 
139
                {
 
140
                        return nameOnly;
 
141
                }
 
142
 
 
143
                /// <returns>True if the pattern should match directories only</returns>
 
144
                public virtual bool DirOnly()
 
145
                {
 
146
                        return dirOnly;
 
147
                }
 
148
 
 
149
                /// <returns>True if the pattern had a "!" in front of it</returns>
 
150
                public virtual bool GetNegation()
 
151
                {
 
152
                        return negation;
 
153
                }
 
154
 
 
155
                /// <returns>The blob pattern to be used as a matcher</returns>
 
156
                public virtual string GetPattern()
 
157
                {
 
158
                        return pattern;
 
159
                }
 
160
 
 
161
                /// <summary>Returns true if a match was made.</summary>
 
162
                /// <remarks>
 
163
                /// Returns true if a match was made.
 
164
                /// <br />
 
165
                /// This function does NOT return the actual ignore status of the
 
166
                /// target! Please consult
 
167
                /// <see cref="GetResult()">GetResult()</see>
 
168
                /// for the ignore status. The actual
 
169
                /// ignore status may be true or false depending on whether this rule is
 
170
                /// an ignore rule or a negation rule.
 
171
                /// </remarks>
 
172
                /// <param name="target">Name pattern of the file, relative to the base directory of this rule
 
173
                ///     </param>
 
174
                /// <param name="isDirectory">Whether the target file is a directory or not</param>
 
175
                /// <returns>
 
176
                /// True if a match was made. This does not necessarily mean that
 
177
                /// the target is ignored. Call
 
178
                /// <see cref="GetResult()">getResult()</see>
 
179
                /// for the result.
 
180
                /// </returns>
 
181
                public virtual bool IsMatch(string target, bool isDirectory)
 
182
                {
 
183
                        if (!target.StartsWith("/"))
 
184
                        {
 
185
                                target = "/" + target;
 
186
                        }
 
187
                        if (matcher == null)
 
188
                        {
 
189
                                if (target.Equals(pattern))
 
190
                                {
 
191
                                        //Exact match
 
192
                                        if (dirOnly && !isDirectory)
 
193
                                        {
 
194
                                                //Directory expectations not met
 
195
                                                return false;
 
196
                                        }
 
197
                                        else
 
198
                                        {
 
199
                                                //Directory expectations met
 
200
                                                return true;
 
201
                                        }
 
202
                                }
 
203
                                if ((target).StartsWith(pattern + "/"))
 
204
                                {
 
205
                                        return true;
 
206
                                }
 
207
                                if (nameOnly)
 
208
                                {
 
209
                                        //Iterate through each sub-name
 
210
                                        string[] segments = target.Split("/");
 
211
                                        for (int idx = 0; idx < segments.Length; idx++)
 
212
                                        {
 
213
                                                string segmentName = segments[idx];
 
214
                                                if (segmentName.Equals(pattern) && DoesMatchDirectoryExpectations(isDirectory, idx
 
215
                                                        , segments.Length))
 
216
                                                {
 
217
                                                        return true;
 
218
                                                }
 
219
                                        }
 
220
                                }
 
221
                        }
 
222
                        else
 
223
                        {
 
224
                                matcher.Append(target);
 
225
                                if (matcher.IsMatch())
 
226
                                {
 
227
                                        return true;
 
228
                                }
 
229
                                string[] segments = target.Split("/");
 
230
                                if (nameOnly)
 
231
                                {
 
232
                                        for (int idx = 0; idx < segments.Length; idx++)
 
233
                                        {
 
234
                                                string segmentName = segments[idx];
 
235
                                                //Iterate through each sub-directory
 
236
                                                matcher.Reset();
 
237
                                                matcher.Append(segmentName);
 
238
                                                if (matcher.IsMatch() && DoesMatchDirectoryExpectations(isDirectory, idx, segments
 
239
                                                        .Length))
 
240
                                                {
 
241
                                                        return true;
 
242
                                                }
 
243
                                        }
 
244
                                }
 
245
                                else
 
246
                                {
 
247
                                        //TODO: This is the slowest operation
 
248
                                        //This matches e.g. "/src/ne?" to "/src/new/file.c"
 
249
                                        matcher.Reset();
 
250
                                        for (int idx = 0; idx < segments.Length; idx++)
 
251
                                        {
 
252
                                                string segmentName = segments[idx];
 
253
                                                if (segmentName.Length > 0)
 
254
                                                {
 
255
                                                        matcher.Append("/" + segmentName);
 
256
                                                }
 
257
                                                if (matcher.IsMatch() && DoesMatchDirectoryExpectations(isDirectory, idx, segments
 
258
                                                        .Length))
 
259
                                                {
 
260
                                                        return true;
 
261
                                                }
 
262
                                        }
 
263
                                }
 
264
                        }
 
265
                        return false;
 
266
                }
 
267
 
 
268
                /// <summary>
 
269
                /// If a call to <code>isMatch(String, boolean)</code> was previously
 
270
                /// made, this will return whether or not the target was ignored.
 
271
                /// </summary>
 
272
                /// <remarks>
 
273
                /// If a call to <code>isMatch(String, boolean)</code> was previously
 
274
                /// made, this will return whether or not the target was ignored. Otherwise
 
275
                /// this just indicates whether the rule is non-negation or negation.
 
276
                /// </remarks>
 
277
                /// <returns>True if the target is to be ignored, false otherwise.</returns>
 
278
                public virtual bool GetResult()
 
279
                {
 
280
                        return !negation;
 
281
                }
 
282
 
 
283
                private bool DoesMatchDirectoryExpectations(bool isDirectory, int segmentIdx, int
 
284
                         segmentLength)
 
285
                {
 
286
                        // The segment we are checking is a directory, expectations are met.
 
287
                        if (segmentIdx < segmentLength - 1)
 
288
                        {
 
289
                                return true;
 
290
                        }
 
291
                        // We are checking the last part of the segment for which isDirectory has to be considered.
 
292
                        return !dirOnly || isDirectory;
 
293
                }
 
294
        }
 
295
}