class BinaryBufferedReader extends BufferedReader {

		private final String ending;

		private final Lock lock = new ReentrantLock();

		private BinaryBufferedReader(Reader in, String ending) {
			super(in);
			this.ending = ending;
		}

		@Override
		public String readLine() throws IOException {

			StringBuilder buffer;

			this.lock.lock();
			try {

				int next = read();
				if (next == -1) {
					return null;
				}

				buffer = new StringBuilder();
				StringBuilder candidateEnding = new StringBuilder();

				while (!isEndOfLine(buffer, candidateEnding, next)) {
					next = read();
				}
				buffer.append(candidateEnding);

			}
			finally {
				this.lock.unlock();
			}

			if (buffer != null && buffer.length() > 0) {
				return buffer.toString();
			}
			return null;

		}

		/**
		 * Check for end of line and accumulate a buffer for next time.
		 * @param buffer the current line excluding the candidate ending
		 * @param candidate a buffer containing accumulated state
		 * @param next the next character (or -1 for end of file)
		 * @return true if the values together signify the end of a file
		 */
		private boolean isEndOfLine(StringBuilder buffer, StringBuilder candidate, int next) {<FILL_FUNCTION_BODY>}

	}


			if (next == -1) {
				return true;
			}

			char c = (char) next;
			if (ending.charAt(0) == c || candidate.length() > 0) {
				candidate.append(c);
			}

			if (candidate.length() == 0) {
				buffer.append(c);
				return false;
			}

			boolean end = ending.equals(candidate.toString());
			if (end) {
				candidate.delete(0, candidate.length());
			}
			else if (candidate.length() >= ending.length()) {
				buffer.append(candidate);
				candidate.delete(0, candidate.length());
			}

			return end;

		
<no_super_class>