changeset 16048:ebdeb414d64c

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
author Michael Van De Vanter <michael.van.de.vanter@oracle.com>
date Thu, 05 Jun 2014 16:17:27 -0700
parents 2662fb9c37e2
children 0498791b33e8
files graal/com.oracle.truffle.api/src/com/oracle/truffle/api/Source.java graal/com.oracle.truffle.api/src/com/oracle/truffle/api/source/SourceFactory.java
diffstat 2 files changed, 146 insertions(+), 21 deletions(-) [+]
line wrap: on
line diff
--- 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();
--- 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 @@
  * <li><strong>Literal Source:</strong> 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.</li>
- * <li><strong>Fake Files:</strong> A named text string used for testing; its contents can be
+ * <li><strong>Pseudo Files:</strong> A named text string used for testing; its contents can be
  * retrieved by name, unlike literal sources.</li>
  * </ul>
  * <p>
@@ -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<String, WeakReference<SourceImpl>> 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, <em>disabled</em> 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<URL, WeakReference<URLSourceImpl>> urlToSource = new HashMap<>();
+
+        public static URLSourceImpl get(URL url, String name) throws IOException {
+            WeakReference<URLSourceImpl> 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;