Switch jgit library to the EDL (3-clause BSD)
[egit/zawir.git] / org.spearce.jgit / src / org / spearce / jgit / transport / RefSpec.java
blob38489beed4453bf15ecdc8648f651390eb642961
1 /*
2 * Copyright (C) 2008, Shawn O. Pearce <spearce@spearce.org>
4 * All rights reserved.
6 * Redistribution and use in source and binary forms, with or
7 * without modification, are permitted provided that the following
8 * conditions are met:
10 * - Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
13 * - Redistributions in binary form must reproduce the above
14 * copyright notice, this list of conditions and the following
15 * disclaimer in the documentation and/or other materials provided
16 * with the distribution.
18 * - Neither the name of the Git Development Community nor the
19 * names of its contributors may be used to endorse or promote
20 * products derived from this software without specific prior
21 * written permission.
23 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
24 * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
25 * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
26 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
27 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
28 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
29 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
30 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
31 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
32 * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
33 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
34 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
35 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
38 package org.spearce.jgit.transport;
40 import org.spearce.jgit.lib.Constants;
41 import org.spearce.jgit.lib.Ref;
43 /**
44 * Describes how refs in one repository copy into another repository.
45 * <p>
46 * A ref specification provides matching support and limited rules to rewrite a
47 * reference in one repository to another reference in another repository.
49 public class RefSpec {
50 private static final String WILDCARD_SUFFIX = "/*";
52 private static boolean isWildcard(final String s) {
53 return s != null && s.endsWith(WILDCARD_SUFFIX);
56 /** Does this specification ask for forced updated (rewind/reset)? */
57 private boolean force;
59 /** Is this specification actually a wildcard match? */
60 private boolean wildcard;
62 /** Name of the ref(s) we would copy from. */
63 private String srcName;
65 /** Name of the ref(s) we would copy into. */
66 private String dstName;
68 /**
69 * Construct an empty RefSpec.
70 * <p>
71 * A newly created empty RefSpec is not suitable for use in most
72 * applications, as at least one field must be set to match a source name.
74 public RefSpec() {
75 force = false;
76 wildcard = false;
77 srcName = Constants.HEAD;
78 dstName = null;
81 /**
82 * Parse a ref specification for use during transport operations.
83 * <p>
84 * Specifications are typically one of the following forms:
85 * <ul>
86 * <li><code>refs/head/master</code></li>
87 * <li><code>refs/head/master:refs/remotes/origin/master</code></li>
88 * <li><code>refs/head/*:refs/remotes/origin/*</code></li>
89 * <li><code>+refs/head/master</code></li>
90 * <li><code>+refs/head/master:refs/remotes/origin/master</code></li>
91 * <li><code>+refs/head/*:refs/remotes/origin/*</code></li>
92 * <li><code>:refs/head/master</code></li>
93 * </ul>
95 * @param spec
96 * string describing the specification.
97 * @throws IllegalArgumentException
98 * the specification is invalid.
100 public RefSpec(final String spec) {
101 String s = spec;
102 if (s.startsWith("+")) {
103 force = true;
104 s = s.substring(1);
107 final int c = s.indexOf(':');
108 if (c == 0) {
109 s = s.substring(1);
110 if (isWildcard(s))
111 throw new IllegalArgumentException("Invalid wildcards " + spec);
112 dstName = s;
113 } else if (c > 0) {
114 srcName = s.substring(0, c);
115 dstName = s.substring(c + 1);
116 if (isWildcard(srcName) && isWildcard(dstName))
117 wildcard = true;
118 else if (isWildcard(srcName) || isWildcard(dstName))
119 throw new IllegalArgumentException("Invalid wildcards " + spec);
120 } else {
121 if (isWildcard(s))
122 throw new IllegalArgumentException("Invalid wildcards " + spec);
123 srcName = s;
128 * Expand a wildcard specification.
130 * @param p
131 * the wildcard specification we should base ourselves on.
132 * @param name
133 * actual name that matched the source of <code>p</code>.
135 protected RefSpec(final RefSpec p, final String name) {
136 final String pdst = p.getDestination();
137 if (p.getSource() == null || pdst == null)
138 throw new IllegalArgumentException("Cannot expand from " + p);
139 force = p.isForceUpdate();
140 srcName = name;
141 dstName = pdst.substring(0, pdst.length() - 1)
142 + name.substring(p.getSource().length() - 1);
145 private RefSpec(final RefSpec p) {
146 force = p.isForceUpdate();
147 wildcard = p.isWildcard();
148 srcName = p.getSource();
149 dstName = p.getDestination();
153 * Check if this specification wants to forcefully update the destination.
155 * @return true if this specification asks for updates without merge tests.
157 public boolean isForceUpdate() {
158 return force;
162 * Create a new RefSpec with a different force update setting.
164 * @param forceUpdate
165 * new value for force update in the returned instance.
166 * @return a new RefSpec with force update as specified.
168 public RefSpec setForceUpdate(final boolean forceUpdate) {
169 final RefSpec r = new RefSpec(this);
170 r.force = forceUpdate;
171 return r;
175 * Check if this specification is actually a wildcard pattern.
176 * <p>
177 * If this is a wildcard pattern then the source and destination names
178 * returned by {@link #getSource()} and {@link #getDestination()} will not
179 * be actual ref names, but instead will be patterns.
181 * @return true if this specification could match more than one ref.
183 public boolean isWildcard() {
184 return wildcard;
188 * Get the source ref description.
189 * <p>
190 * During a fetch this is the name of the ref on the remote repository we
191 * are fetching from. During a push this is the name of the ref on the local
192 * repository we are pushing out from.
194 * @return name (or wildcard pattern) to match the source ref.
196 public String getSource() {
197 return srcName;
201 * Create a new RefSpec with a different source name setting.
203 * @param source
204 * new value for source in the returned instance.
205 * @return a new RefSpec with source as specified.
206 * @throws IllegalStateException
207 * There is already a destination configured, and the wildcard
208 * status of the existing destination disagrees with the
209 * wildcard status of the new source.
211 public RefSpec setSource(final String source) {
212 final RefSpec r = new RefSpec(this);
213 r.srcName = source;
214 if (isWildcard(r.srcName) && r.dstName == null)
215 throw new IllegalStateException("Destination is not a wildcard.");
216 if (isWildcard(r.srcName) != isWildcard(r.dstName))
217 throw new IllegalStateException("Source/Destination must match.");
218 return r;
222 * Get the destination ref description.
223 * <p>
224 * During a fetch this is the local tracking branch that will be updated
225 * with the new ObjectId after feching is complete. During a push this is
226 * the remote ref that will be updated by the remote's receive-pack process.
227 * <p>
228 * If null during a fetch no tracking branch should be updated and the
229 * ObjectId should be stored transiently in order to prepare a merge.
230 * <p>
231 * If null during a push, use {@link #getSource()} instead.
233 * @return name (or wildcard) pattern to match the destination ref.
235 public String getDestination() {
236 return dstName;
240 * Create a new RefSpec with a different destination name setting.
242 * @param destination
243 * new value for destination in the returned instance.
244 * @return a new RefSpec with destination as specified.
245 * @throws IllegalStateException
246 * There is already a source configured, and the wildcard status
247 * of the existing source disagrees with the wildcard status of
248 * the new destination.
250 public RefSpec setDestination(final String destination) {
251 final RefSpec r = new RefSpec(this);
252 r.dstName = destination;
253 if (isWildcard(r.dstName) && r.srcName == null)
254 throw new IllegalStateException("Source is not a wildcard.");
255 if (isWildcard(r.srcName) != isWildcard(r.dstName))
256 throw new IllegalStateException("Source/Destination must match.");
257 return r;
261 * Create a new RefSpec with a different source/destination name setting.
263 * @param source
264 * new value for source in the returned instance.
265 * @param destination
266 * new value for destination in the returned instance.
267 * @return a new RefSpec with destination as specified.
268 * @throws IllegalArgumentException
269 * The wildcard status of the new source disagrees with the
270 * wildcard status of the new destination.
272 public RefSpec setSourceDestination(final String source,
273 final String destination) {
274 if (isWildcard(source) != isWildcard(destination))
275 throw new IllegalArgumentException("Source/Destination must match.");
276 final RefSpec r = new RefSpec(this);
277 r.wildcard = isWildcard(source);
278 r.srcName = source;
279 r.dstName = destination;
280 return r;
284 * Does this specification's source description match the ref?
286 * @param r
287 * ref whose name should be tested.
288 * @return true if the names match; false otherwise.
290 public boolean matchSource(final Ref r) {
291 return match(r, getSource());
295 * Does this specification's destination description match the ref?
297 * @param r
298 * ref whose name should be tested.
299 * @return true if the names match; false otherwise.
301 public boolean matchDestination(final Ref r) {
302 return match(r, getDestination());
306 * Expand this specification to exactly match a ref.
307 * <p>
308 * Callers must first verify the passed ref matches this specification,
309 * otherwise expansion results may be unpredictable.
311 * @param r
312 * a ref that matched our source specification.
313 * @return a new specification that is not a wildcard.
315 public RefSpec expandFromSource(final Ref r) {
316 return isWildcard() ? new RefSpec(this, r.getName()) : this;
319 private boolean match(final Ref r, final String s) {
320 if (s == null)
321 return false;
322 if (isWildcard())
323 return r.getName().startsWith(s.substring(0, s.length() - 1));
324 return r.getName().equals(s);
327 public int hashCode() {
328 int hc = 0;
329 if (getSource() != null)
330 hc = hc * 31 + getSource().hashCode();
331 if (getDestination() != null)
332 hc = hc * 31 + getDestination().hashCode();
333 return hc;
336 public boolean equals(final Object obj) {
337 if (!(obj instanceof RefSpec))
338 return false;
339 final RefSpec b = (RefSpec) obj;
340 if (isForceUpdate() != b.isForceUpdate())
341 return false;
342 if (isWildcard() != b.isWildcard())
343 return false;
344 if (!eq(getSource(), b.getSource()))
345 return false;
346 if (!eq(getDestination(), b.getDestination()))
347 return false;
348 return true;
351 private static boolean eq(final String a, final String b) {
352 if (a == b)
353 return true;
354 if (a == null || b == null)
355 return false;
356 return a.equals(b);
359 public String toString() {
360 final StringBuilder r = new StringBuilder();
361 if (isForceUpdate())
362 r.append('+');
363 if (getSource() != null)
364 r.append(getSource());
365 if (getDestination() != null) {
366 r.append(':');
367 r.append(getDestination());
369 return r.toString();