View Javadoc
1   /*
2    * Copyright (C) 2019, Google LLC. and others
3    *
4    * This program and the accompanying materials are made available under the
5    * terms of the Eclipse Distribution License v. 1.0 which is available at
6    * https://www.eclipse.org/org/documents/edl-v10.php.
7    *
8    * SPDX-License-Identifier: BSD-3-Clause
9    */
10  
11  package org.eclipse.jgit.transport;
12  
13  import java.text.MessageFormat;
14  
15  import org.eclipse.jgit.annotations.Nullable;
16  import org.eclipse.jgit.errors.PackProtocolException;
17  import org.eclipse.jgit.internal.JGitText;
18  
19  /**
20   * Represents either a filter specified in a protocol "filter" line, or a
21   * placeholder to indicate no filtering.
22   *
23   * @since 5.4
24   */
25  public final class FilterSpec {
26  
27  	private final long blobLimit;
28  
29  	private final long treeDepthLimit;
30  
31  	private FilterSpec(long blobLimit, long treeDepthLimit) {
32  		this.blobLimit = blobLimit;
33  		this.treeDepthLimit = treeDepthLimit;
34  	}
35  
36  	/**
37  	 * Process the content of "filter" line from the protocol. It has a shape
38  	 * like:
39  	 *
40  	 * <ul>
41  	 *   <li>"blob:none"
42  	 *   <li>"blob:limit=N", with N &gt;= 0
43  	 *   <li>"tree:DEPTH", with DEPTH &gt;= 0
44  	 * </ul>
45  	 *
46  	 * @param filterLine
47  	 *            the content of the "filter" line in the protocol
48  	 * @return a FilterSpec representing the given filter
49  	 * @throws PackProtocolException
50  	 *             invalid filter because due to unrecognized format or
51  	 *             negative/non-numeric filter.
52  	 */
53  	public static FilterSpec fromFilterLine(String filterLine)
54  			throws PackProtocolException {
55  		if (filterLine.equals("blob:none")) { //$NON-NLS-1$
56  			return FilterSpec.withBlobLimit(0);
57  		} else if (filterLine.startsWith("blob:limit=")) { //$NON-NLS-1$
58  			long blobLimit = -1;
59  			try {
60  				blobLimit = Long
61  						.parseLong(filterLine.substring("blob:limit=".length())); //$NON-NLS-1$
62  			} catch (NumberFormatException e) {
63  				// Do not change blobLimit so that we throw a
64  				// PackProtocolException later.
65  			}
66  			if (blobLimit >= 0) {
67  				return FilterSpec.withBlobLimit(blobLimit);
68  			}
69  		} else if (filterLine.startsWith("tree:")) { //$NON-NLS-1$
70  			long treeDepthLimit = -1;
71  			try {
72  				treeDepthLimit = Long
73  						.parseLong(filterLine.substring("tree:".length())); //$NON-NLS-1$
74  			} catch (NumberFormatException e) {
75  				// Do not change blobLimit so that we throw a
76  				// PackProtocolException later.
77  			}
78  			if (treeDepthLimit >= 0) {
79  				return FilterSpec.withTreeDepthLimit(treeDepthLimit);
80  			}
81  		}
82  
83  		// Did not match any known filter format.
84  		throw new PackProtocolException(
85  				MessageFormat.format(JGitText.get().invalidFilter, filterLine));
86  	}
87  
88  	/**
89  	 * @param blobLimit
90  	 *            the blob limit in a "blob:[limit]" or "blob:none" filter line
91  	 * @return a filter spec which filters blobs above a certain size
92  	 */
93  	static FilterSpec withBlobLimit(long blobLimit) {
94  		if (blobLimit < 0) {
95  			throw new IllegalArgumentException(
96  					"blobLimit cannot be negative: " + blobLimit); //$NON-NLS-1$
97  		}
98  		return new FilterSpec(blobLimit, -1);
99  	}
100 
101 	/**
102 	 * @param treeDepthLimit
103 	 *            the tree depth limit in a "tree:[depth]" filter line
104 	 * @return a filter spec which filters blobs and trees beyond a certain tree
105 	 *         depth
106 	 */
107 	static FilterSpec withTreeDepthLimit(long treeDepthLimit) {
108 		if (treeDepthLimit < 0) {
109 			throw new IllegalArgumentException(
110 					"treeDepthLimit cannot be negative: " + treeDepthLimit); //$NON-NLS-1$
111 		}
112 		return new FilterSpec(-1, treeDepthLimit);
113 	}
114 
115 	/**
116 	 * A placeholder that indicates no filtering.
117 	 */
118 	public static final FilterSpecec.html#FilterSpec">FilterSpec NO_FILTER = new FilterSpec(-1, -1);
119 
120 	/**
121 	 * @return -1 if this filter does not filter blobs based on size, or a
122 	 *         non-negative integer representing the max size of blobs to allow
123 	 */
124 	public long getBlobLimit() {
125 		return blobLimit;
126 	}
127 
128 	/**
129 	 * @return -1 if this filter does not filter blobs and trees based on depth,
130 	 *         or a non-negative integer representing the max tree depth of
131 	 *         blobs and trees to fetch
132 	 */
133 	public long getTreeDepthLimit() {
134 		return treeDepthLimit;
135 	}
136 
137 	/**
138 	 * @return true if this filter doesn't filter out anything
139 	 */
140 	public boolean isNoOp() {
141 		return blobLimit == -1 && treeDepthLimit == -1;
142 	}
143 
144 	/**
145 	 * @return the filter line which describes this spec, e.g. "filter blob:limit=42"
146 	 */
147 	@Nullable
148 	public String filterLine() {
149 		if (blobLimit == 0) {
150 			return GitProtocolConstants.OPTION_FILTER + " blob:none"; //$NON-NLS-1$
151 		}
152 
153 		if (blobLimit > 0) {
154 			return GitProtocolConstants.OPTION_FILTER + " blob:limit=" + blobLimit; //$NON-NLS-1$
155 		}
156 
157 		return null;
158 	}
159 }