View Javadoc
1   /*
2    * Copyright (C) 2015, 2020 Ivan Motsch <ivan.motsch@bsiag.com> 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.util.io;
12  
13  import java.io.InputStream;
14  import java.io.OutputStream;
15  import java.util.EnumSet;
16  
17  import org.eclipse.jgit.attributes.Attributes;
18  import org.eclipse.jgit.lib.CoreConfig.EolStreamType;
19  import org.eclipse.jgit.treewalk.TreeWalk.OperationType;
20  import org.eclipse.jgit.treewalk.WorkingTreeOptions;
21  import org.eclipse.jgit.util.SystemReader;
22  import org.eclipse.jgit.util.io.AutoLFInputStream.StreamFlag;
23  
24  /**
25   * Utility used to create input and output stream wrappers for
26   * {@link org.eclipse.jgit.lib.CoreConfig.EolStreamType}
27   *
28   * @since 4.3
29   */
30  public final class EolStreamTypeUtil {
31  
32  	private EolStreamTypeUtil() {
33  	}
34  
35  	/**
36  	 * Convenience method used to detect if CRLF conversion has been configured
37  	 * using the
38  	 * <ul>
39  	 * <li>global repo options</li>
40  	 * <li>global attributes</li>
41  	 * <li>info attributes</li>
42  	 * <li>working tree .gitattributes</li>
43  	 * </ul>
44  	 *
45  	 * @param op
46  	 *            is the
47  	 *            {@link org.eclipse.jgit.treewalk.TreeWalk.OperationType} of
48  	 *            the current traversal
49  	 * @param options
50  	 *            are the {@link org.eclipse.jgit.lib.Config} options with key
51  	 *            {@link org.eclipse.jgit.treewalk.WorkingTreeOptions#KEY}
52  	 * @param attrs
53  	 *            are the {@link org.eclipse.jgit.attributes.Attributes} of the
54  	 *            file for which the
55  	 *            {@link org.eclipse.jgit.lib.CoreConfig.EolStreamType} is to be
56  	 *            detected
57  	 * @return the stream conversion
58  	 *         {@link org.eclipse.jgit.lib.CoreConfig.EolStreamType} to be
59  	 *         performed for the selected
60  	 *         {@link org.eclipse.jgit.treewalk.TreeWalk.OperationType}
61  	 */
62  	public static EolStreamType detectStreamType(OperationType op,
63  			WorkingTreeOptions options, Attributes attrs) {
64  		switch (op) {
65  		case CHECKIN_OP:
66  			return checkInStreamType(options, attrs);
67  		case CHECKOUT_OP:
68  			return checkOutStreamType(options, attrs);
69  		default:
70  			throw new IllegalArgumentException("unknown OperationType " + op); //$NON-NLS-1$
71  		}
72  	}
73  
74  	/**
75  	 * Wrap the input stream depending on
76  	 * {@link org.eclipse.jgit.lib.CoreConfig.EolStreamType}.
77  	 *
78  	 * @param in
79  	 *            original stream
80  	 * @param conversion
81  	 *            to be performed
82  	 * @return the converted stream depending on
83  	 *         {@link org.eclipse.jgit.lib.CoreConfig.EolStreamType}
84  	 */
85  	public static InputStream wrapInputStream(InputStream in,
86  			EolStreamType conversion) {
87  		return wrapInputStream(in, conversion, false);
88  	}
89  
90  	/**
91  	 * Wrap the input stream depending on
92  	 * {@link org.eclipse.jgit.lib.CoreConfig.EolStreamType}.
93  	 *
94  	 * @param in
95  	 *            original stream
96  	 * @param conversion
97  	 *            to be performed
98  	 * @param forCheckout
99  	 *            whether the stream is for checking out from the repository
100 	 * @return the converted stream depending on
101 	 *         {@link org.eclipse.jgit.lib.CoreConfig.EolStreamType}
102 	 * @since 5.9
103 	 */
104 	public static InputStream wrapInputStream(InputStream in,
105 			EolStreamType conversion, boolean forCheckout) {
106 		switch (conversion) {
107 		case TEXT_CRLF:
108 			return new AutoCRLFInputStream(in, false);
109 		case TEXT_LF:
110 			return AutoLFInputStream.create(in);
111 		case AUTO_CRLF:
112 			return new AutoCRLFInputStream(in, true);
113 		case AUTO_LF:
114 			EnumSet<StreamFlag> flags = forCheckout
115 					? EnumSet.of(StreamFlag.DETECT_BINARY,
116 							StreamFlag.FOR_CHECKOUT)
117 					: EnumSet.of(StreamFlag.DETECT_BINARY);
118 			return new AutoLFInputStream(in, flags);
119 		default:
120 			return in;
121 		}
122 	}
123 
124 	/**
125 	 * Wrap the output stream depending on
126 	 * {@link org.eclipse.jgit.lib.CoreConfig.EolStreamType}.
127 	 *
128 	 * @param out
129 	 *            original stream
130 	 * @param conversion
131 	 *            to be performed
132 	 * @return the converted stream depending on
133 	 *         {@link org.eclipse.jgit.lib.CoreConfig.EolStreamType}
134 	 */
135 	public static OutputStream wrapOutputStream(OutputStream out,
136 			EolStreamType conversion) {
137 		switch (conversion) {
138 		case TEXT_CRLF:
139 			return new AutoCRLFOutputStream(out, false);
140 		case AUTO_CRLF:
141 			return new AutoCRLFOutputStream(out, true);
142 		case TEXT_LF:
143 			return new AutoLFOutputStream(out, false);
144 		case AUTO_LF:
145 			return new AutoLFOutputStream(out, true);
146 		default:
147 			return out;
148 		}
149 	}
150 
151 	private static EolStreamType checkInStreamType(WorkingTreeOptions options,
152 			Attributes attrs) {
153 		if (attrs.isUnset("text")) {//$NON-NLS-1$
154 			// "binary" or "-text" (which is included in the binary expansion)
155 			return EolStreamType.DIRECT;
156 		}
157 
158 		// old git system
159 		if (attrs.isSet("crlf")) {//$NON-NLS-1$
160 			return EolStreamType.TEXT_LF; // Same as isSet("text")
161 		} else if (attrs.isUnset("crlf")) {//$NON-NLS-1$
162 			return EolStreamType.DIRECT; // Same as isUnset("text")
163 		} else if ("input".equals(attrs.getValue("crlf"))) {//$NON-NLS-1$ //$NON-NLS-2$
164 			return EolStreamType.TEXT_LF; // Same as eol=lf
165 		}
166 
167 		// new git system
168 		if ("auto".equals(attrs.getValue("text"))) { //$NON-NLS-1$ //$NON-NLS-2$
169 			return EolStreamType.AUTO_LF;
170 		}
171 
172 		String eol = attrs.getValue("eol"); //$NON-NLS-1$
173 		if (eol != null) {
174 			// check-in is always normalized to LF
175 			return EolStreamType.TEXT_LF;
176 		}
177 		if (attrs.isSet("text")) { //$NON-NLS-1$
178 			return EolStreamType.TEXT_LF;
179 		}
180 
181 		switch (options.getAutoCRLF()) {
182 		case TRUE:
183 		case INPUT:
184 			return EolStreamType.AUTO_LF;
185 		case FALSE:
186 			return EolStreamType.DIRECT;
187 		}
188 
189 		return EolStreamType.DIRECT;
190 	}
191 
192 	private static EolStreamType getOutputFormat(WorkingTreeOptions options) {
193 		switch (options.getAutoCRLF()) {
194 		case TRUE:
195 			return EolStreamType.TEXT_CRLF;
196 		case INPUT:
197 			return EolStreamType.DIRECT;
198 		default:
199 			// no decision
200 		}
201 		switch (options.getEOL()) {
202 		case CRLF:
203 			return EolStreamType.TEXT_CRLF;
204 		case NATIVE:
205 			if (SystemReader.getInstance().isWindows()) {
206 				return EolStreamType.TEXT_CRLF;
207 			}
208 			return EolStreamType.TEXT_LF;
209 		case LF:
210 		default:
211 			break;
212 		}
213 		return EolStreamType.DIRECT;
214 	}
215 
216 	private static EolStreamType checkOutStreamType(WorkingTreeOptions options,
217 			Attributes attrs) {
218 		if (attrs.isUnset("text")) {//$NON-NLS-1$
219 			// "binary" or "-text" (which is included in the binary expansion)
220 			return EolStreamType.DIRECT;
221 		}
222 
223 		// old git system
224 		if (attrs.isSet("crlf")) {//$NON-NLS-1$
225 			return getOutputFormat(options); // Same as isSet("text")
226 		} else if (attrs.isUnset("crlf")) {//$NON-NLS-1$
227 			return EolStreamType.DIRECT; // Same as isUnset("text")
228 		} else if ("input".equals(attrs.getValue("crlf"))) {//$NON-NLS-1$ //$NON-NLS-2$
229 			return EolStreamType.DIRECT; // Same as eol=lf
230 		}
231 
232 		// new git system
233 		String eol = attrs.getValue("eol"); //$NON-NLS-1$
234 		if (eol != null) {
235 			if ("crlf".equals(eol)) { //$NON-NLS-1$
236 				if ("auto".equals(attrs.getValue("text"))) { //$NON-NLS-1$ //$NON-NLS-2$
237 					return EolStreamType.AUTO_CRLF;
238 				}
239 				return EolStreamType.TEXT_CRLF;
240 			} else if ("lf".equals(eol)) { //$NON-NLS-1$
241 				return EolStreamType.DIRECT;
242 			}
243 		}
244 		if (attrs.isSet("text")) { //$NON-NLS-1$
245 			return getOutputFormat(options);
246 		}
247 
248 		if ("auto".equals(attrs.getValue("text"))) { //$NON-NLS-1$ //$NON-NLS-2$
249 			EolStreamType basic = getOutputFormat(options);
250 			switch (basic) {
251 			case TEXT_CRLF:
252 				return EolStreamType.AUTO_CRLF;
253 			case TEXT_LF:
254 				return EolStreamType.AUTO_LF;
255 			default:
256 				return basic;
257 			}
258 		}
259 
260 		switch (options.getAutoCRLF()) {
261 		case TRUE:
262 			return EolStreamType.AUTO_CRLF;
263 		default:
264 			// no decision
265 		}
266 
267 		return EolStreamType.DIRECT;
268 	}
269 
270 }