1 /*******************************************************************************
2  * Copyright (c) 2000, 2018 IBM Corporation and others.
3  *
4  * This program and the accompanying materials
5  * are made available under the terms of the Eclipse Public License 2.0
6  * which accompanies this distribution, and is available at
7  * https://www.eclipse.org/legal/epl-2.0/
8  *
9  * SPDX-License-Identifier: EPL-2.0
10  *
11  * Contributors:
12  *     IBM Corporation - initial API and implementation
13  *     Brock Janiczak <brockj@tpg.com.au> - Bug 181919 LineReader creating unneeded garbage
14  *******************************************************************************/
15 package org.eclipse.compare.internal.core.patch;
16 
17 import java.io.BufferedReader;
18 import java.io.IOException;
19 import java.util.*;
20 
21 import org.eclipse.compare.internal.core.ComparePlugin;
22 import org.eclipse.compare.patch.ReaderCreator;
23 import org.eclipse.core.runtime.Assert;
24 import org.eclipse.core.runtime.CoreException;
25 
26 public class LineReader {
27 	/**
28 	 * Reads the contents and returns them as a List of lines.
29 	 */
load(ReaderCreator content, boolean create)30 	public static List<String> load(ReaderCreator content, boolean create) {
31 		List<String> lines = null;
32 		BufferedReader bufferedReader = null;
33 		if (!create && content != null && content.canCreateReader()) {
34 			// read current contents
35 			try {
36 				bufferedReader = new BufferedReader(content.createReader());
37 				lines = readLines(bufferedReader);
38 			} catch (CoreException ex) {
39 				ComparePlugin.log(ex);
40 			} finally {
41 				if (bufferedReader != null) {
42 					try {
43 						bufferedReader.close();
44 					} catch (IOException ex) {
45 						// silently ignored
46 					}
47 				}
48 			}
49 		}
50 
51 		if (lines == null)
52 			lines = new ArrayList<>();
53 		return lines;
54 	}
55 
readLines(BufferedReader reader)56 	public static List<String> readLines(BufferedReader reader) {
57 		List<String> lines;
58 		LineReader lr= new LineReader(reader);
59 		lr.ignoreSingleCR(); // Don't treat single CRs as line feeds to be consistent with command line patch
60 		lines= lr.readLines();
61 		return lines;
62 	}
63 
64 	/*
65 	 * Concatenates all strings found in the given List.
66 	 */
createString(boolean preserveLineDelimeters, List<String> lines)67 	public static String createString(boolean preserveLineDelimeters, List<String> lines) {
68 		StringBuilder sb= new StringBuilder();
69 		Iterator<String> iter= lines.iterator();
70 		if (preserveLineDelimeters) {
71 			while (iter.hasNext())
72 				sb.append(iter.next());
73 		} else {
74 			String lineSeparator= System.getProperty("line.separator"); //$NON-NLS-1$
75 			while (iter.hasNext()) {
76 				String line= iter.next();
77 				int l= length(line);
78 				if (l < line.length()) {	// line has delimiter
79 					sb.append(line.substring(0, l));
80 					sb.append(lineSeparator);
81 				} else {
82 					sb.append(line);
83 				}
84 			}
85 		}
86 		return sb.toString();
87 	}
88 
89 	/*
90 	 * Returns the length (excluding a line delimiter CR, LF, CR/LF)
91 	 * of the given string.
92 	 */
length(String s)93 	static int length(String s) {
94 		int l= s.length();
95 		if (l > 0) {
96 			char c= s.charAt(l-1);
97 			if (c == '\r')
98 				return l-1;
99 			if (c == '\n') {
100 				if (l > 1 && s.charAt(l-2) == '\r')
101 					return l-2;
102 				return l-1;
103 			}
104 		}
105 		return l;
106 	}
107 
108 	private boolean fHaveChar= false;
109 	private int fLastChar;
110 	private boolean fSawEOF= false;
111 	private BufferedReader fReader;
112 	private boolean fIgnoreSingleCR= false;
113 	private StringBuilder fBuffer= new StringBuilder();
114 
LineReader(BufferedReader reader)115 	public LineReader(BufferedReader reader) {
116 		this.fReader= reader;
117 		Assert.isNotNull(reader);
118 	}
119 
ignoreSingleCR()120 	public void ignoreSingleCR() {
121 		this.fIgnoreSingleCR= true;
122 	}
123 
124 	/**
125 	 * Reads a line of text. A line is considered to be terminated by any one
126 	 * of a line feed ('\n'), a carriage return ('\r'), or a carriage return
127 	 * followed immediately by a line-feed.
128 	 * @return A string containing the contents of the line including
129 	 *	the line-termination characters, or <code>null</code> if the end of the
130 	 *	stream has been reached
131 	 * @exception IOException If an I/O error occurs
132 	 */
readLine()133 	String readLine() throws IOException {
134 		try {
135 			while (!this.fSawEOF) {
136 				int c= readChar();
137 				if (c == -1) {
138 					this.fSawEOF= true;
139 					break;
140 				}
141 				this.fBuffer.append((char)c);
142 				if (c == '\n')
143 					break;
144 				if (c == '\r') {
145 					c= readChar();
146 					if (c == -1) {
147 						this.fSawEOF= true;
148 						break;	// EOF
149 					}
150 					if (c != '\n') {
151 						if (this.fIgnoreSingleCR) {
152 							this.fBuffer.append((char)c);
153 							continue;
154 						}
155 						this.fHaveChar= true;
156 						this.fLastChar= c;
157 					} else
158 						this.fBuffer.append((char)c);
159 					break;
160 				}
161 			}
162 
163 			if (this.fBuffer.length() != 0) {
164 				return this.fBuffer.toString();
165 			}
166 			return null;
167 		} finally {
168 			this.fBuffer.setLength(0);
169 		}
170 	}
171 
close()172 	void close() {
173 		try {
174 			this.fReader.close();
175 		} catch (IOException ex) {
176 			// silently ignored
177 		}
178 	}
179 
readLines()180 	public List<String> readLines() {
181 		try {
182 			List<String> lines= new ArrayList<>();
183 			String line;
184 			while ((line= readLine()) != null)
185 				lines.add(line);
186 			return lines;
187 		} catch (IOException ex) {
188 			// NeedWork
189 			//System.out.println("error while reading file: " + fileName + "(" + ex + ")");
190 		} finally {
191 			close();
192 		}
193 		return null;
194 	}
195 
196 	/*
197 	 * Returns the number of characters in the given string without
198 	 * counting a trailing line separator.
199 	 */
lineContentLength(String line)200 	int lineContentLength(String line) {
201 		if (line == null)
202 			return 0;
203 		int length= line.length();
204 		for (int i= length-1; i >= 0; i--) {
205 			char c= line.charAt(i);
206 			if (c =='\n' || c == '\r')
207 				length--;
208 			else
209 				break;
210 		}
211 		return length;
212 	}
213 
214 	//---- private
215 
readChar()216 	private int readChar() throws IOException {
217 		if (this.fHaveChar) {
218 			this.fHaveChar= false;
219 			return this.fLastChar;
220 		}
221 		return this.fReader.read();
222 	}
223 }
224