Define RemoteConfig and RefSpec objects to parse remote blocks
[egit/zawir.git] / org.spearce.jgit / src / org / spearce / jgit / transport / RefSpec.java
blobc501b68b7368090fd93c1674770de812e59fd956
1 /*
2 * Copyright (C) 2008 Shawn Pearce <spearce@spearce.org>
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU General Public
6 * License, version 2, as published by the Free Software Foundation.
8 * This library is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
11 * General Public License for more details.
13 * You should have received a copy of the GNU General Public
14 * License along with this library; if not, write to the Free Software
15 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301
17 package org.spearce.jgit.transport;
19 import org.spearce.jgit.lib.Constants;
20 import org.spearce.jgit.lib.Ref;
22 /**
23 * Describes how refs in one repository copy into another repository.
24 * <p>
25 * A ref specification provides matching support and limited rules to rewrite a
26 * reference in one repository to another reference in another repository.
28 public class RefSpec {
29 private static final String WILDCARD_SUFFIX = "/*";
31 private static boolean isWildcard(final String s) {
32 return s != null && s.endsWith(WILDCARD_SUFFIX);
35 /** Does this specification ask for forced updated (rewind/reset)? */
36 private boolean force;
38 /** Is this specification actually a wildcard match? */
39 private boolean wildcard;
41 /** Name of the ref(s) we would copy from. */
42 private String srcName;
44 /** Name of the ref(s) we would copy into. */
45 private String dstName;
47 /**
48 * Construct an empty RefSpec.
49 * <p>
50 * A newly created empty RefSpec is not suitable for use in most
51 * applications, as at least one field must be set to match a source name.
53 public RefSpec() {
54 force = false;
55 wildcard = false;
56 srcName = Constants.HEAD;
57 dstName = null;
60 /**
61 * Parse a ref specification for use during transport operations.
62 * <p>
63 * Specifications are typically one of the following forms:
64 * <ul>
65 * <li><code>refs/head/master</code></li>
66 * <li><code>refs/head/master:refs/remotes/origin/master</code></li>
67 * <li><code>refs/head/*:refs/remotes/origin/*</code></li>
68 * <li><code>+refs/head/master</code></li>
69 * <li><code>+refs/head/master:refs/remotes/origin/master</code></li>
70 * <li><code>+refs/head/*:refs/remotes/origin/*</code></li>
71 * <li><code>:refs/head/master</code></li>
72 * </ul>
74 * @param spec
75 * string describing the specification.
76 * @throws IllegalArgumentException
77 * the specification is invalid.
79 public RefSpec(final String spec) {
80 String s = spec;
81 if (s.startsWith("+")) {
82 force = true;
83 s = s.substring(1);
86 final int c = s.indexOf(':');
87 if (c == 0) {
88 s = s.substring(1);
89 if (isWildcard(s))
90 throw new IllegalArgumentException("Invalid wildcards " + spec);
91 dstName = s;
92 } else if (c > 0) {
93 srcName = s.substring(0, c);
94 dstName = s.substring(c + 1);
95 if (isWildcard(srcName) && isWildcard(dstName))
96 wildcard = true;
97 else if (isWildcard(srcName) || isWildcard(dstName))
98 throw new IllegalArgumentException("Invalid wildcards " + spec);
99 } else {
100 if (isWildcard(s))
101 throw new IllegalArgumentException("Invalid wildcards " + spec);
102 srcName = s;
107 * Expand a wildcard specification.
109 * @param p
110 * the wildcard specification we should base ourselves on.
111 * @param name
112 * actual name that matched the source of <code>p</code>.
114 protected RefSpec(final RefSpec p, final String name) {
115 final String pdst = p.getDestination();
116 if (p.getSource() == null || pdst == null)
117 throw new IllegalArgumentException("Cannot expand from " + p);
118 force = p.isForceUpdate();
119 srcName = name;
120 dstName = pdst.substring(0, pdst.length() - 1)
121 + name.substring(p.getSource().length() - 1);
124 private RefSpec(final RefSpec p) {
125 force = p.isForceUpdate();
126 wildcard = p.isWildcard();
127 srcName = p.getSource();
128 dstName = p.getDestination();
132 * Check if this specification wants to forcefully update the destination.
134 * @return true if this specification asks for updates without merge tests.
136 public boolean isForceUpdate() {
137 return force;
141 * Create a new RefSpec with a different force update setting.
143 * @param forceUpdate
144 * new value for force update in the returned instance.
145 * @return a new RefSpec with force update as specified.
147 public RefSpec setForceUpdate(final boolean forceUpdate) {
148 final RefSpec r = new RefSpec(this);
149 r.force = forceUpdate;
150 return r;
154 * Check if this specification is actually a wildcard pattern.
155 * <p>
156 * If this is a wildcard pattern then the source and destination names
157 * returned by {@link #getSource()} and {@link #getDestination()} will not
158 * be actual ref names, but instead will be patterns.
160 * @return true if this specification could match more than one ref.
162 public boolean isWildcard() {
163 return wildcard;
167 * Get the source ref description.
168 * <p>
169 * During a fetch this is the name of the ref on the remote repository we
170 * are fetching from. During a push this is the name of the ref on the local
171 * repository we are pushing out from.
173 * @return name (or wildcard pattern) to match the source ref.
175 public String getSource() {
176 return srcName;
180 * Create a new RefSpec with a different source name setting.
182 * @param source
183 * new value for source in the returned instance.
184 * @return a new RefSpec with source as specified.
185 * @throws IllegalStateException
186 * There is already a destination configured, and the wildcard
187 * status of the existing destination disagrees with the
188 * wildcard status of the new source.
190 public RefSpec setSource(final String source) {
191 final RefSpec r = new RefSpec(this);
192 r.srcName = source;
193 if (isWildcard(r.srcName) && r.dstName == null)
194 throw new IllegalStateException("Destination is not a wildcard.");
195 if (isWildcard(r.srcName) != isWildcard(r.dstName))
196 throw new IllegalStateException("Source/Destination must match.");
197 return r;
201 * Get the destination ref description.
202 * <p>
203 * During a fetch this is the local tracking branch that will be updated
204 * with the new ObjectId after feching is complete. During a push this is
205 * the remote ref that will be updated by the remote's receive-pack process.
206 * <p>
207 * If null during a fetch no tracking branch should be updated and the
208 * ObjectId should be stored transiently in order to prepare a merge.
209 * <p>
210 * If null during a push, use {@link #getSource()} instead.
212 * @return name (or wildcard) pattern to match the destination ref.
214 public String getDestination() {
215 return dstName;
219 * Create a new RefSpec with a different destination name setting.
221 * @param destination
222 * new value for destination in the returned instance.
223 * @return a new RefSpec with destination as specified.
224 * @throws IllegalStateException
225 * There is already a source configured, and the wildcard status
226 * of the existing source disagrees with the wildcard status of
227 * the new destination.
229 public RefSpec setDestination(final String destination) {
230 final RefSpec r = new RefSpec(this);
231 r.dstName = destination;
232 if (isWildcard(r.dstName) && r.srcName == null)
233 throw new IllegalStateException("Source is not a wildcard.");
234 if (isWildcard(r.srcName) != isWildcard(r.dstName))
235 throw new IllegalStateException("Source/Destination must match.");
236 return r;
240 * Create a new RefSpec with a different source/destination name setting.
242 * @param source
243 * new value for source in the returned instance.
244 * @param destination
245 * new value for destination in the returned instance.
246 * @return a new RefSpec with destination as specified.
247 * @throws IllegalArgumentException
248 * The wildcard status of the new source disagrees with the
249 * wildcard status of the new destination.
251 public RefSpec setSourceDestination(final String source,
252 final String destination) {
253 if (isWildcard(source) != isWildcard(destination))
254 throw new IllegalArgumentException("Source/Destination must match.");
255 final RefSpec r = new RefSpec(this);
256 r.wildcard = isWildcard(source);
257 r.srcName = source;
258 r.dstName = destination;
259 return r;
263 * Does this specification's source description match the ref?
265 * @param r
266 * ref whose name should be tested.
267 * @return true if the names match; false otherwise.
269 public boolean matchSource(final Ref r) {
270 return match(r, getSource());
274 * Does this specification's destination description match the ref?
276 * @param r
277 * ref whose name should be tested.
278 * @return true if the names match; false otherwise.
280 public boolean matchDestination(final Ref r) {
281 return match(r, getDestination());
285 * Expand this specification to exactly match a ref.
286 * <p>
287 * Callers must first verify the passed ref matches this specification,
288 * otherwise expansion results may be unpredictable.
290 * @param r
291 * a ref that matched our source specification.
292 * @return a new specification that is not a wildcard.
294 public RefSpec expandFromSource(final Ref r) {
295 return isWildcard() ? new RefSpec(this, r.getName()) : this;
298 private boolean match(final Ref r, final String s) {
299 if (s == null)
300 return false;
301 if (isWildcard())
302 return r.getName().startsWith(s.substring(0, s.length() - 1));
303 return r.getName().equals(s);
306 public int hashCode() {
307 int hc = 0;
308 if (getSource() != null)
309 hc = hc * 31 + getSource().hashCode();
310 if (getDestination() != null)
311 hc = hc * 31 + getDestination().hashCode();
312 return hc;
315 public boolean equals(final Object obj) {
316 if (!(obj instanceof RefSpec))
317 return false;
318 final RefSpec b = (RefSpec) obj;
319 if (isForceUpdate() != b.isForceUpdate())
320 return false;
321 if (isWildcard() != b.isWildcard())
322 return false;
323 if (!eq(getSource(), b.getSource()))
324 return false;
325 if (!eq(getDestination(), b.getDestination()))
326 return false;
327 return true;
330 private static boolean eq(final String a, final String b) {
331 if (a == b)
332 return true;
333 if (a == null || b == null)
334 return false;
335 return a.equals(b);
338 public String toString() {
339 final StringBuilder r = new StringBuilder();
340 if (isForceUpdate())
341 r.append('+');
342 if (getSource() != null)
343 r.append(getSource());
344 if (getDestination() != null) {
345 r.append(':');
346 r.append(getDestination());
348 return r.toString();