# HG changeset patch # User Michael Van De Vanter # Date 1402010247 25200 # Node ID ebdeb414d64cf5304c7811ade92e93f6b9975b6b # Parent 2662fb9c37e2fcc699df6eb2f2d814dc8eada25e Truffle/Source: - clean up where IOException gets thrown for unreadable files - provide for sources from URLs - rename SourceFactory.asFakeFile() to asPseudoFile() - TruffleSourceFactory.asNull(String name) creates a named placeholder for a source that is unavailable diff -r 2662fb9c37e2 -r ebdeb414d64c graal/com.oracle.truffle.api/src/com/oracle/truffle/api/Source.java --- a/graal/com.oracle.truffle.api/src/com/oracle/truffle/api/Source.java Thu Jun 05 20:33:33 2014 +0200 +++ b/graal/com.oracle.truffle.api/src/com/oracle/truffle/api/Source.java Thu Jun 05 16:17:27 2014 -0700 @@ -25,11 +25,12 @@ package com.oracle.truffle.api; import java.io.*; +import java.net.*; import com.oracle.truffle.api.source.*; /** - * Represents a unit (typically a file) of guest language source code. + * Represents a unit of guest language source code. */ public interface Source { @@ -51,11 +52,16 @@ String getShortName(); /** - * The normalized, canonical name of the file. + * The normalized, canonical name if the source is a file. */ String getPath(); /** + * The URL if the source is retrieved via URL. + */ + URL getURL(); + + /** * Access to the source contents. */ Reader getReader(); diff -r 2662fb9c37e2 -r ebdeb414d64c graal/com.oracle.truffle.api/src/com/oracle/truffle/api/source/SourceFactory.java --- a/graal/com.oracle.truffle.api/src/com/oracle/truffle/api/source/SourceFactory.java Thu Jun 05 20:33:33 2014 +0200 +++ b/graal/com.oracle.truffle.api/src/com/oracle/truffle/api/source/SourceFactory.java Thu Jun 05 16:17:27 2014 -0700 @@ -26,6 +26,7 @@ import java.io.*; import java.lang.ref.*; +import java.net.*; import java.util.*; import com.oracle.truffle.api.*; @@ -39,7 +40,7 @@ *
  • Literal Source: A named text string, whose contents are supplied concretely * (possibly via an {@link Reader}), can also be used as a source. These are not indexed and should * be considered value objects; equality is defined based on contents.
  • - *
  • Fake Files: A named text string used for testing; its contents can be + *
  • Pseudo Files: A named text string used for testing; its contents can be * retrieved by name, unlike literal sources.
  • * *

    @@ -56,7 +57,7 @@ */ public final class SourceFactory { - // Only files and fake files are indexed. + // Only files and pseudo files are indexed. private static final Map> pathToSource = new HashMap<>(); private static boolean fileCacheEnabled = true; @@ -71,23 +72,17 @@ * @param fileName name * @param reset forces any existing {@link Source} cache to be cleared, forcing a re-read * @return canonical representation of the file's contents. - * @throws RuntimeException if the file can not be read + * @throws IOException if the file can not be read */ - public static Source fromFile(String fileName, boolean reset) throws RuntimeException { - - // TODO (mlvdv) throw IOException + public static Source fromFile(String fileName, boolean reset) throws IOException { SourceImpl source = lookup(fileName); if (source == null) { final File file = new File(fileName); - String path = null; - if (file.exists()) { - try { - path = file.getCanonicalPath(); - } catch (IOException e) { - throw new RuntimeException("Can't find file " + fileName); - } + if (!file.canRead()) { + throw new IOException("Can't read file " + fileName); } + String path = file.getCanonicalPath(); source = lookup(path); if (source == null) { source = new FileSourceImpl(file, fileName, path); @@ -106,9 +101,9 @@ * * @param fileName name * @return canonical representation of the file's contents. - * @throws RuntimeException if the file can not be read + * @throws IOException if the file can not be read */ - public static Source fromFile(String fileName) throws RuntimeException { + public static Source fromFile(String fileName) throws IOException { return fromFile(fileName, false); } @@ -125,6 +120,18 @@ } /** + * Creates a source whose contents will be read immediately from a URL and cached. + * + * @param url + * @param name identifies the origin, possibly useful for debugging + * @return a newly created, non-indexed source representation + * @throws IOException if reading fails + */ + public static Source fromURL(URL url, String name) throws IOException { + return URLSourceImpl.get(url, name); + } + + /** * Creates a source whose contents will be read immediately and cached. * * @param reader @@ -141,16 +148,28 @@ * (unlike other literal sources); intended for testing. * * @param code textual source code - * @param fakeFileName string to use for indexing/lookup + * @param pseudoFileName string to use for indexing/lookup * @return a newly created, source representation, canonical with respect to its name */ - public static Source asFakeFile(String code, String fakeFileName) { - final SourceImpl source = new LiteralSourceImpl(fakeFileName, code); - store(fakeFileName, source); + public static Source asPseudoFile(String code, String pseudoFileName) { + final SourceImpl source = new LiteralSourceImpl(pseudoFileName, code); + store(pseudoFileName, source); return source; } /** + * Creates a non-canonical representation of a source that is unavailable; attempts to access + * any information about the source beyond the name/description will result in a + * {@link IllegalStateException} + * + * @param description a note about the origin, for error messages and debugging + * @return a newly created, non-indexed representation of an unavailable source + */ + public static Source asNull(String description) { + return new NullSourceImpl(description); + } + + /** * Enables/disables caching of file contents, disabled by default. Caching of sources * created from literal text or readers is always enabled. */ @@ -302,6 +321,10 @@ return name; } + public URL getURL() { + return null; + } + @Override public Reader getReader() { return new StringReader(code); @@ -386,6 +409,10 @@ return path; } + public URL getURL() { + return null; + } + @Override public Reader getReader() { if (code != null && timeStamp == file.lastModified()) { @@ -405,6 +432,98 @@ } + private static class URLSourceImpl extends SourceImpl { + + private static final Map> urlToSource = new HashMap<>(); + + public static URLSourceImpl get(URL url, String name) throws IOException { + WeakReference sourceRef = urlToSource.get(url); + URLSourceImpl source = sourceRef == null ? null : sourceRef.get(); + if (source == null) { + source = new URLSourceImpl(url, name); + urlToSource.put(url, new WeakReference<>(source)); + } + return source; + } + + private final URL url; + private final String name; + private String code = null; // A cache of the source contents + + public URLSourceImpl(URL url, String name) throws IOException { + this.url = url; + this.name = name; + code = read(new InputStreamReader(url.openStream())); + } + + public String getName() { + return name; + } + + public String getShortName() { + return name; + } + + public String getPath() { + return url.getPath(); + } + + public URL getURL() { + return url; + } + + public Reader getReader() { + return new StringReader(code); + } + + public String getCode() { + return code; + } + + @Override + protected void reset() { + } + + } + + private static final class NullSourceImpl extends SourceImpl { + + private final String description; + + public NullSourceImpl(String description) { + this.description = description; + } + + public String getName() { + return description; + } + + public String getShortName() { + return description; + } + + public String getPath() { + return null; + } + + public URL getURL() { + return null; + } + + public Reader getReader() { + return null; + } + + public String getCode() { + throw new IllegalStateException("null source: " + description); + } + + @Override + protected void reset() { + } + + } + private static class SourceSectionImpl implements SourceSection { private final Source source;