Skip to content

Commit efc1316

Browse files
committed
chore: adapt scala.io.Source#RelaxedPosition.this
Copy of the file from scala 2.13.16
1 parent 6c9f7bb commit efc1316

File tree

2 files changed

+382
-1
lines changed

2 files changed

+382
-1
lines changed

project/Scala2LibraryBootstrappedMiMaFilters.scala

-1
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,6 @@ object Scala2LibraryBootstrappedMiMaFilters {
7070
ProblemFilters.exclude[MissingFieldProblem]("scala.runtime.NonLocalReturnControl.value"),
7171
) ++
7272
Seq( // DirectMissingMethodProblem
73-
"scala.io.Source#RelaxedPosition.this",
7473
"scala.collection.IterableOnceOps#Maximized.this", // New in 2.13.11: private inner class
7574
"scala.util.Properties.<clinit>",
7675
"scala.util.Sorting.scala$util$Sorting$$mergeSort$default$5",
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,382 @@
1+
/*
2+
* Scala (https://www.scala-lang.org)
3+
*
4+
* Copyright EPFL and Lightbend, Inc. dba Akka
5+
*
6+
* Licensed under Apache License 2.0
7+
* (http://www.apache.org/licenses/LICENSE-2.0).
8+
*
9+
* See the NOTICE file distributed with this work for
10+
* additional information regarding copyright ownership.
11+
*/
12+
13+
package scala
14+
package io
15+
16+
import scala.collection.{AbstractIterator, BufferedIterator}
17+
import java.io.{Closeable, FileInputStream, FileNotFoundException, InputStream, PrintStream, File => JFile}
18+
import java.net.{URI, URL}
19+
20+
import scala.annotation.nowarn
21+
22+
/** This object provides convenience methods to create an iterable
23+
* representation of a source file.
24+
*/
25+
object Source {
26+
val DefaultBufSize = 2048
27+
28+
/** Creates a `Source` from System.in.
29+
*/
30+
def stdin = fromInputStream(System.in)
31+
32+
/** Creates a Source from an Iterable.
33+
*
34+
* @param iterable the Iterable
35+
* @return the Source
36+
*/
37+
def fromIterable(iterable: Iterable[Char]): Source = new Source {
38+
val iter = iterable.iterator
39+
} withReset(() => fromIterable(iterable))
40+
41+
/** Creates a Source instance from a single character.
42+
*/
43+
def fromChar(c: Char): Source = fromIterable(Array(c))
44+
45+
/** creates Source from array of characters, with empty description.
46+
*/
47+
def fromChars(chars: Array[Char]): Source = fromIterable(chars)
48+
49+
/** creates Source from a String, with no description.
50+
*/
51+
def fromString(s: String): Source = fromIterable(s)
52+
53+
/** creates Source from file with given name, setting its description to
54+
* filename.
55+
*/
56+
def fromFile(name: String)(implicit codec: Codec): BufferedSource =
57+
fromFile(new JFile(name))(codec)
58+
59+
/** creates Source from file with given name, using given encoding, setting
60+
* its description to filename.
61+
*/
62+
def fromFile(name: String, enc: String): BufferedSource =
63+
fromFile(name)(Codec(enc))
64+
65+
/** creates `source` from file with given file `URI`.
66+
*/
67+
def fromFile(uri: URI)(implicit codec: Codec): BufferedSource =
68+
fromFile(new JFile(uri))(codec)
69+
70+
/** creates Source from file with given file: URI
71+
*/
72+
def fromFile(uri: URI, enc: String): BufferedSource =
73+
fromFile(uri)(Codec(enc))
74+
75+
/** creates Source from file, using default character encoding, setting its
76+
* description to filename.
77+
*/
78+
def fromFile(file: JFile)(implicit codec: Codec): BufferedSource =
79+
fromFile(file, Source.DefaultBufSize)(codec)
80+
81+
/** same as fromFile(file, enc, Source.DefaultBufSize)
82+
*/
83+
def fromFile(file: JFile, enc: String): BufferedSource =
84+
fromFile(file)(Codec(enc))
85+
86+
def fromFile(file: JFile, enc: String, bufferSize: Int): BufferedSource =
87+
fromFile(file, bufferSize)(Codec(enc))
88+
89+
/** Creates Source from `file`, using given character encoding, setting
90+
* its description to filename. Input is buffered in a buffer of size
91+
* `bufferSize`.
92+
*/
93+
def fromFile(file: JFile, bufferSize: Int)(implicit codec: Codec): BufferedSource = {
94+
val inputStream = new FileInputStream(file)
95+
96+
createBufferedSource(
97+
inputStream,
98+
bufferSize,
99+
() => fromFile(file, bufferSize)(codec),
100+
() => inputStream.close()
101+
)(codec) withDescription s"file:${file.getAbsolutePath}"
102+
}
103+
104+
/** Create a `Source` from array of bytes, decoding
105+
* the bytes according to codec.
106+
*
107+
* @return the created `Source` instance.
108+
*/
109+
def fromBytes(bytes: Array[Byte])(implicit codec: Codec): Source =
110+
fromString(new String(bytes, codec.name))
111+
112+
def fromBytes(bytes: Array[Byte], enc: String): Source =
113+
fromBytes(bytes)(Codec(enc))
114+
115+
/** Create a `Source` from array of bytes, assuming
116+
* one byte per character (ISO-8859-1 encoding.)
117+
*/
118+
@deprecated("Use `fromBytes` and specify an encoding", since="2.13.9")
119+
def fromRawBytes(bytes: Array[Byte]): Source =
120+
fromString(new String(bytes, Codec.ISO8859.charSet))
121+
122+
/** creates `Source` from file with given file: URI
123+
*/
124+
def fromURI(uri: URI)(implicit codec: Codec): BufferedSource =
125+
fromFile(new JFile(uri))(codec)
126+
127+
/** same as fromURL(new URL(s))(Codec(enc))
128+
*/
129+
def fromURL(s: String, enc: String): BufferedSource =
130+
fromURL(s)(Codec(enc))
131+
132+
/** same as fromURL(new URL(s))
133+
*/
134+
def fromURL(s: String)(implicit codec: Codec): BufferedSource =
135+
fromURL(new URI(s).toURL)(codec)
136+
137+
/** same as fromInputStream(url.openStream())(Codec(enc))
138+
*/
139+
def fromURL(url: URL, enc: String): BufferedSource =
140+
fromURL(url)(Codec(enc))
141+
142+
/** same as fromInputStream(url.openStream())(codec)
143+
*/
144+
def fromURL(url: URL)(implicit codec: Codec): BufferedSource =
145+
fromInputStream(url.openStream())(codec)
146+
147+
/** Reads data from inputStream with a buffered reader, using the encoding
148+
* in implicit parameter codec.
149+
*
150+
* @param inputStream the input stream from which to read
151+
* @param bufferSize buffer size (defaults to Source.DefaultBufSize)
152+
* @param reset a () => Source which resets the stream (if unset, reset() will throw an Exception)
153+
* @param close a () => Unit method which closes the stream (if unset, close() will do nothing)
154+
* @param codec (implicit) a scala.io.Codec specifying behavior (defaults to Codec.default)
155+
* @return the buffered source
156+
*/
157+
def createBufferedSource(
158+
inputStream: InputStream,
159+
bufferSize: Int = DefaultBufSize,
160+
reset: () => Source = null,
161+
close: () => Unit = null
162+
)(implicit codec: Codec): BufferedSource = {
163+
// workaround for default arguments being unable to refer to other parameters
164+
val resetFn = if (reset == null) () => createBufferedSource(inputStream, bufferSize, reset, close)(codec) else reset
165+
166+
new BufferedSource(inputStream, bufferSize)(codec) withReset resetFn withClose close
167+
}
168+
169+
def fromInputStream(is: InputStream, enc: String): BufferedSource =
170+
fromInputStream(is)(Codec(enc))
171+
172+
def fromInputStream(is: InputStream)(implicit codec: Codec): BufferedSource =
173+
createBufferedSource(is, reset = () => fromInputStream(is)(codec), close = () => is.close())(codec)
174+
175+
/** Reads data from a classpath resource, using either a context classloader (default) or a passed one.
176+
*
177+
* @param resource name of the resource to load from the classpath
178+
* @param classLoader classloader to be used, or context classloader if not specified
179+
* @return the buffered source
180+
*/
181+
def fromResource(resource: String, classLoader: ClassLoader = Thread.currentThread().getContextClassLoader())(implicit codec: Codec): BufferedSource =
182+
Option(classLoader.getResourceAsStream(resource)) match {
183+
case Some(in) => fromInputStream(in)
184+
case None => throw new FileNotFoundException(s"resource '$resource' was not found in the classpath from the given classloader.")
185+
}
186+
187+
}
188+
189+
/** An iterable representation of source data.
190+
* It may be reset with the optional [[reset]] method.
191+
*
192+
* Subclasses must supply [[scala.io.Source.iter the underlying iterator]].
193+
*
194+
* Error handling may be customized by overriding the [[scala.io.Source.report report]] method.
195+
*
196+
* The [[scala.io.Source.ch current input]] and [[scala.io.Source.pos position]],
197+
* as well as the [[scala.io.Source.next next character]] methods delegate to
198+
* [[scala.io.Source#Positioner the positioner]].
199+
*
200+
* The default positioner encodes line and column numbers in the position passed to [[report]].
201+
* This behavior can be changed by supplying a
202+
* [[scala.io.Source.withPositioning(pos:* custom positioner]].
203+
*
204+
*/
205+
abstract class Source extends Iterator[Char] with Closeable {
206+
/** the actual iterator */
207+
protected val iter: Iterator[Char]
208+
209+
// ------ public values
210+
211+
/** description of this source, default empty */
212+
var descr: String = ""
213+
var nerrors = 0
214+
var nwarnings = 0
215+
216+
private def lineNum(line: Int): String = (getLines() drop (line - 1) take 1).mkString
217+
218+
class LineIterator extends AbstractIterator[String] with Iterator[String] {
219+
private[this] val sb = new StringBuilder
220+
221+
lazy val iter: BufferedIterator[Char] = Source.this.iter.buffered
222+
def isNewline(ch: Char): Boolean = ch == '\r' || ch == '\n'
223+
def getc(): Boolean = iter.hasNext && {
224+
val ch = iter.next()
225+
if (ch == '\n') false
226+
else if (ch == '\r') {
227+
if (iter.hasNext && iter.head == '\n')
228+
iter.next()
229+
230+
false
231+
}
232+
else {
233+
sb append ch
234+
true
235+
}
236+
}
237+
def hasNext: Boolean = iter.hasNext
238+
def next(): String = {
239+
sb.clear()
240+
while (getc()) { }
241+
sb.toString
242+
}
243+
}
244+
245+
/** Returns an iterator who returns lines (NOT including newline character(s)).
246+
* It will treat any of \r\n, \r, or \n as a line separator (longest match) - if
247+
* you need more refined behavior you can subclass Source#LineIterator directly.
248+
*/
249+
def getLines(): Iterator[String] = new LineIterator()
250+
251+
/** Returns `'''true'''` if this source has more characters.
252+
*/
253+
def hasNext: Boolean = iter.hasNext
254+
255+
/** Returns next character.
256+
*/
257+
def next(): Char = positioner.next()
258+
259+
@nowarn("cat=deprecation")
260+
class Positioner(encoder: Position) {
261+
def this() = this(RelaxedPosition)
262+
/** the last character returned by next. */
263+
var ch: Char = _
264+
265+
/** position of last character returned by next */
266+
var pos = 0
267+
268+
/** current line and column */
269+
var cline = 1
270+
var ccol = 1
271+
272+
/** default col increment for tabs '\t', set to 4 initially */
273+
var tabinc = 4
274+
275+
def next(): Char = {
276+
ch = iter.next()
277+
pos = encoder.encode(cline, ccol)
278+
ch match {
279+
case '\n' =>
280+
ccol = 1
281+
cline += 1
282+
case '\t' =>
283+
ccol += tabinc
284+
case _ =>
285+
ccol += 1
286+
}
287+
ch
288+
}
289+
}
290+
/** A Position implementation which ignores errors in
291+
* the positions.
292+
*/
293+
@nowarn("cat=deprecation")
294+
object RelaxedPosition extends Position {
295+
private val _ = Source.this
296+
def checkInput(line: Int, column: Int): Unit = ()
297+
}
298+
object RelaxedPositioner extends Positioner(RelaxedPosition) { }
299+
object NoPositioner extends Positioner(Position) {
300+
override def next(): Char = iter.next()
301+
}
302+
def ch: Char = positioner.ch
303+
def pos: Int = positioner.pos
304+
305+
/** Reports an error message to the output stream `out`.
306+
*
307+
* @param pos the source position (line/column)
308+
* @param msg the error message to report
309+
* @param out PrintStream to use (optional: defaults to `Console.err`)
310+
*/
311+
def reportError(
312+
pos: Int,
313+
msg: String,
314+
out: PrintStream = Console.err): Unit =
315+
{
316+
nerrors += 1
317+
report(pos, msg, out)
318+
}
319+
320+
private def spaces(n: Int) = List.fill(n)(' ').mkString
321+
/**
322+
* @param pos the source position (line/column)
323+
* @param msg the error message to report
324+
* @param out PrintStream to use
325+
*/
326+
def report(pos: Int, msg: String, out: PrintStream): Unit = {
327+
val line = Position line pos
328+
val col = Position column pos
329+
330+
out println "%s:%d:%d: %s%s%s^".format(descr, line, col, msg, lineNum(line), spaces(col - 1))
331+
}
332+
333+
/**
334+
* @param pos the source position (line/column)
335+
* @param msg the warning message to report
336+
* @param out PrintStream to use (optional: defaults to `Console.out`)
337+
*/
338+
def reportWarning(
339+
pos: Int,
340+
msg: String,
341+
out: PrintStream = Console.out): Unit =
342+
{
343+
nwarnings += 1
344+
report(pos, "warning! " + msg, out)
345+
}
346+
347+
private[this] var resetFunction: () => Source = null
348+
private[this] var closeFunction: () => Unit = null
349+
private[this] var positioner: Positioner = RelaxedPositioner
350+
351+
def withReset(f: () => Source): this.type = {
352+
resetFunction = f
353+
this
354+
}
355+
def withClose(f: () => Unit): this.type = {
356+
closeFunction = f
357+
this
358+
}
359+
def withDescription(text: String): this.type = {
360+
descr = text
361+
this
362+
}
363+
/** Change or disable the positioner. */
364+
def withPositioning(on: Boolean): this.type = {
365+
positioner = if (on) RelaxedPositioner else NoPositioner
366+
this
367+
}
368+
def withPositioning(pos: Positioner): this.type = {
369+
positioner = pos
370+
this
371+
}
372+
373+
/** The close() method closes the underlying resource. */
374+
def close(): Unit = {
375+
if (closeFunction != null) closeFunction()
376+
}
377+
378+
/** The reset() method creates a fresh copy of this Source. */
379+
def reset(): Source =
380+
if (resetFunction != null) resetFunction()
381+
else throw new UnsupportedOperationException("Source's reset() method was not set.")
382+
}

0 commit comments

Comments
 (0)