diff truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/source/Source.java @ 22104:cf19259edf87

TruffleVM.eval and Source.withMimeType
author Jaroslav Tulach <jaroslav.tulach@oracle.com>
date Mon, 24 Aug 2015 08:46:21 +0200
parents 04b13488da2a
children 7ee578004be7
line wrap: on
line diff
--- a/truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/source/Source.java	Mon Aug 24 08:25:31 2015 +0200
+++ b/truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/source/Source.java	Mon Aug 24 08:46:21 2015 +0200
@@ -32,6 +32,11 @@
 import java.util.*;
 
 import com.oracle.truffle.api.*;
+import com.oracle.truffle.api.TruffleLanguage.Registration;
+import java.nio.file.Files;
+import java.nio.file.spi.FileTypeDetector;
+import java.util.logging.Level;
+import java.util.logging.Logger;
 
 /**
  * Representation of a guest language source code unit and its contents. Sources originate in
@@ -87,6 +92,7 @@
  * <p>
  */
 public abstract class Source {
+    private static final Logger LOG = Logger.getLogger(Source.class.getName());
 
     // TODO (mlvdv) consider canonicalizing and reusing SourceSection instances
     // TOOD (mlvdv) connect SourceSections into a spatial tree for fast geometric lookup
@@ -374,7 +380,8 @@
     private Source() {
     }
 
-    private TextMap textMap = null;
+    private String mimeType;
+    private TextMap textMap;
 
     abstract void reset();
 
@@ -402,6 +409,8 @@
 
     /**
      * The URL if the source is retrieved via URL.
+     * 
+     * @return URL or <code>null</code>
      */
     public abstract URL getURL();
 
@@ -632,7 +641,50 @@
         return TextMap.fromString(code);
     }
 
-    private static final class LiteralSource extends Source {
+    /**
+     * Associates the source with specified MIME type. The mime type may be used to select the right
+     * {@link Registration Truffle language} to use to execute the returned source. The value of MIME
+     * type can be obtained via {@link #getMimeType()} method.
+     *
+     * @param mime mime type to use
+     * @return new (identical) source, just associated {@link #getMimeType()}
+     */
+    public final Source withMimeType(String mime) {
+        try {
+            Source another = (Source) clone();
+            another.mimeType = mime;
+            return another;
+        } catch (CloneNotSupportedException ex) {
+            throw new IllegalStateException(ex);
+        }
+    }
+
+    /**
+     * MIME type that is associated with this source. By default file extensions known to the system
+     * are used to determine the MIME type (via registered {@link FileTypeDetector} classes), yet
+     * one can directly {@link #withMimeType(java.lang.String) provide a MIME type} to each source.
+     *
+     * @return MIME type of this source or <code>null</code>, if unknown
+     */
+    public String getMimeType() {
+        if (mimeType == null) {
+            mimeType = findMimeType();
+        }
+        return mimeType;
+    }
+
+    String findMimeType() {
+        return null;
+    }
+
+    final boolean equalMime(Source other) {
+        if (mimeType == null) {
+            return other.mimeType == null;
+        }
+        return mimeType.equals(other.mimeType);
+    }
+
+    private static final class LiteralSource extends Source implements Cloneable {
 
         private final String description;
         private final String code;
@@ -691,14 +743,14 @@
             }
             if (obj instanceof LiteralSource) {
                 LiteralSource other = (LiteralSource) obj;
-                return description.equals(other.description) && code.equals(other.code);
+                return description.equals(other.description) && code.equals(other.code) && equalMime(other);
             }
             return false;
         }
     }
 
-    private static final class AppendableLiteralSource extends Source {
-        private String description;
+    private static final class AppendableLiteralSource extends Source implements Cloneable {
+        private final String description;
         final List<CharSequence> codeList = new ArrayList<>();
 
         public AppendableLiteralSource(String description) {
@@ -756,7 +808,7 @@
 
     }
 
-    private static final class FileSource extends Source {
+    private static final class FileSource extends Source implements Cloneable {
 
         private final File file;
         private final String name; // Name used originally to describe the source
@@ -836,13 +888,29 @@
         }
 
         @Override
+        String findMimeType() {
+            if (file.getName().endsWith(".c")) {
+                return "text/x-c";
+            } else if (file.getName().endsWith(".R") || file.getName().endsWith(".r")) {
+                return "application/x-r";
+            } else {
+                try {
+                    return Files.probeContentType(file.toPath());
+                } catch (IOException ex) {
+                    LOG.log(Level.SEVERE, null, ex);
+                }
+            }
+            return null;
+        }
+
+        @Override
         public boolean equals(Object obj) {
             if (this == obj) {
                 return true;
             }
             if (obj instanceof FileSource) {
                 FileSource other = (FileSource) obj;
-                return path.equals(other.path);
+                return path.equals(other.path) && equalMime(other);
             }
             return false;
         }
@@ -854,7 +922,7 @@
     }
 
     // TODO (mlvdv) if we keep this, hoist a superclass in common with FileSource.
-    private static final class ClientManagedFileSource extends Source {
+    private static final class ClientManagedFileSource extends Source implements Cloneable {
 
         private final File file;
         private final String name; // Name used originally to describe the source
@@ -908,6 +976,22 @@
         }
 
         @Override
+        String findMimeType() {
+            if (file.getName().endsWith(".c")) {
+                return "text/x-c";
+            } else if (file.getName().endsWith(".R") || file.getName().endsWith(".r")) {
+                return "application/x-r";
+            } else {
+                try {
+                    return Files.probeContentType(file.toPath());
+                } catch (IOException ex) {
+                    LOG.log(Level.SEVERE, null, ex);
+                }
+            }
+            return null;
+        }
+
+        @Override
         public int hashCode() {
             return path.hashCode();
         }
@@ -919,7 +1003,7 @@
             }
             if (obj instanceof ClientManagedFileSource) {
                 ClientManagedFileSource other = (ClientManagedFileSource) obj;
-                return path.equals(other.path);
+                return path.equals(other.path) && equalMime(other);
             }
             return false;
         }
@@ -930,7 +1014,7 @@
         }
     }
 
-    private static final class URLSource extends Source {
+    private static final class URLSource extends Source implements Cloneable {
 
         private static final Map<URL, WeakReference<URLSource>> urlToSource = new HashMap<>();
 
@@ -946,12 +1030,16 @@
 
         private final URL url;
         private final String name;
-        private String code = null;  // A cache of the source contents
+        private String code;  // A cache of the source contents
 
         public URLSource(URL url, String name) throws IOException {
             this.url = url;
             this.name = name;
-            code = read(new InputStreamReader(url.openStream()));
+            URLConnection c = url.openConnection();
+            if (super.mimeType == null) {
+                super.mimeType = c.getContentType();
+            }
+            code = read(new InputStreamReader(c.getInputStream()));
         }
 
         @Override
@@ -989,7 +1077,7 @@
         }
     }
 
-    private static final class SubSource extends Source {
+    private static final class SubSource extends Source implements Cloneable {
         private final Source base;
         private final int baseIndex;
         private final int subLength;
@@ -1044,7 +1132,7 @@
         }
     }
 
-    private static final class BytesSource extends Source {
+    private static final class BytesSource extends Source implements Cloneable {
 
         private final String name;
         private final byte[] bytes;