changeset 21271:1516d26e8f2b

Truffle/Source: add a new kind of Source (both indexed and non-indexed flavors) whose contents are unavailable at creation, but will be provided incrementally.
author Michael Van De Vanter <michael.van.de.vanter@oracle.com>
date Thu, 07 May 2015 20:14:23 -0700
parents a43c7adc9d99
children 090291af4230
files graal/com.oracle.truffle.api/src/com/oracle/truffle/api/source/Source.java
diffstat 1 files changed, 153 insertions(+), 21 deletions(-) [+]
line wrap: on
line diff
--- a/graal/com.oracle.truffle.api/src/com/oracle/truffle/api/source/Source.java	Thu May 07 17:51:55 2015 -0700
+++ b/graal/com.oracle.truffle.api/src/com/oracle/truffle/api/source/Source.java	Thu May 07 20:14:23 2015 -0700
@@ -60,6 +60,11 @@
  * <li><strong>Reader:</strong> Contents are <em>read eagerly</em> and treated as an anonymous
  * (non-indexed) <em>Literal</em> . <br>
  * See {@link Source#fromReader(Reader, String)}</li>
+ * <p>
+ * <li><strong>AppendableSource:</strong> Literal contents are provided by the client,
+ * incrementally, after the instance is created.<br>
+ * See {@link Source#fromAppendableText(String)}<br>
+ * See {@link Source#fromNamedAppendableText(String)}</li>
  * </ul>
  * <p>
  * <strong>File cache:</strong>
@@ -239,6 +244,19 @@
     }
 
     /**
+     * Creates an anonymous source from literal text that is provided incrementally after creation:
+     * not named and not indexed.
+     *
+     * @param description a note about the origin, for error messages and debugging
+     * @return a newly created, non-indexed, initially empty, appendable source representation
+     */
+    public static AppendableSource fromAppendableText(String description) {
+        final AppendableSource source = new AppendableLiteralSource(description);
+        notifyNewSource(source).tagAs(Tags.FROM_LITERAL);
+        return source;
+    }
+
+    /**
      * Creates a source from literal text that can be retrieved by name, with no assumptions about
      * the structure or meaning of the name. If the name is already in the index, the new instance
      * will replace the previously existing instance in the index.
@@ -255,6 +273,22 @@
     }
 
     /**
+     * Creates a source from literal text that is provided incrementally after creation and which
+     * can be retrieved by name, with no assumptions about the structure or meaning of the name. If
+     * the name is already in the index, the new instance will replace the previously existing
+     * instance in the index.
+     *
+     * @param name string to use for indexing/lookup
+     * @return a newly created, indexed, initially empty, appendable source representation
+     */
+    public static AppendableSource fromNamedAppendableText(String name) {
+        final Source source = new AppendableLiteralSource(name);
+        nameToSource.put(name, new WeakReference<>(source));
+        notifyNewSource(source).tagAs(Tags.FROM_LITERAL);
+        return (AppendableSource) source;
+    }
+
+    /**
      * Creates a source whose contents will be read immediately from a URL and cached.
      *
      * @param url
@@ -384,9 +418,28 @@
         return builder.toString();
     }
 
+    public abstract static class AppendableSource extends Source {
+
+        /**
+         * Sets the mark.
+         */
+        public void setMark() {
+        }
+
+        public abstract void appendCode(CharSequence chars);
+
+        /**
+         * Gets the code from the mark to the end.
+         */
+        public String getCodeFromMark() {
+            return getCode();
+        }
+
+    }
+
     private final ArrayList<SourceTag> tags = new ArrayList<>();
 
-    Source() {
+    private Source() {
     }
 
     private TextMap textMap = null;
@@ -641,13 +694,25 @@
         return new LineLocationImpl(this, lineNumber);
     }
 
-    private TextMap checkTextMap() {
+    /**
+     * An object suitable for using as a key into a hashtable that defines equivalence between
+     * different source types.
+     */
+    protected Object getHashKey() {
+        return getName();
+    }
+
+    protected final TextMap checkTextMap() {
         if (textMap == null) {
             textMap = createTextMap();
         }
         return textMap;
     }
 
+    protected final void clearTextMap() {
+        textMap = null;
+    }
+
     protected TextMap createTextMap() {
         final String code = getCode();
         if (code == null) {
@@ -658,22 +723,22 @@
 
     private static final class LiteralSource extends Source {
 
-        private final String name; // Name used originally to describe the source
+        private final String description;
         private final String code;
 
-        public LiteralSource(String name, String code) {
-            this.name = name;
+        public LiteralSource(String description, String code) {
+            this.description = description;
             this.code = code;
         }
 
         @Override
         public String getName() {
-            return name;
+            return description;
         }
 
         @Override
         public String getShortName() {
-            return name;
+            return description;
         }
 
         @Override
@@ -683,7 +748,7 @@
 
         @Override
         public String getPath() {
-            return name;
+            return description;
         }
 
         @Override
@@ -702,11 +767,7 @@
 
         @Override
         public int hashCode() {
-            final int prime = 31;
-            int result = 1;
-            result = prime * result + name.hashCode();
-            result = prime * result + (code == null ? 0 : code.hashCode());
-            return result;
+            return description.hashCode();
         }
 
         @Override
@@ -717,13 +778,81 @@
             if (obj == null) {
                 return false;
             }
-            if (!(obj instanceof LiteralSource)) {
-                return false;
+            if (obj instanceof LiteralSource) {
+                LiteralSource other = (LiteralSource) obj;
+                return description.equals(other.description);
             }
-            LiteralSource other = (LiteralSource) obj;
-            return name.equals(other.name) && code.equals(other.code);
+            return false;
+        }
+    }
+
+    private static final class AppendableLiteralSource extends AppendableSource {
+        private String description;
+        private int mark = 0;
+        final List<CharSequence> codeList = new ArrayList<>();
+
+        public AppendableLiteralSource(String description) {
+            this.description = description;
+        }
+
+        @Override
+        public String getName() {
+            return description;
+        }
+
+        @Override
+        public String getShortName() {
+            return description;
+        }
+
+        @Override
+        public String getCode() {
+            return getCodeFromIndex(0);
+        }
+
+        @Override
+        public String getPath() {
+            return description;
         }
 
+        @Override
+        public URL getURL() {
+            return null;
+        }
+
+        @Override
+        public Reader getReader() {
+            return new StringReader(getCode());
+        }
+
+        @Override
+        protected void reset() {
+        }
+
+        private String getCodeFromIndex(int index) {
+            StringBuilder sb = new StringBuilder();
+            for (int i = index; i < codeList.size(); i++) {
+                CharSequence s = codeList.get(i);
+                sb.append(s);
+            }
+            return sb.toString();
+        }
+
+        @Override
+        public String getCodeFromMark() {
+            return getCodeFromIndex(mark);
+        }
+
+        @Override
+        public void appendCode(CharSequence chars) {
+            codeList.add(chars);
+            clearTextMap();
+        }
+
+        @Override
+        public void setMark() {
+            mark = codeList.size();
+        }
     }
 
     private static final class FileSource extends Source {
@@ -759,6 +888,11 @@
         }
 
         @Override
+        protected Object getHashKey() {
+            return path;
+        }
+
+        @Override
         public String getCode() {
             if (fileCacheEnabled) {
                 if (code == null || timeStamp != file.lastModified()) {
@@ -821,7 +955,6 @@
         protected void reset() {
             this.code = null;
         }
-
     }
 
     private static final class URLSource extends Source {
@@ -881,7 +1014,6 @@
         @Override
         protected void reset() {
         }
-
     }
 
     private static final class BytesSource extends Source {
@@ -1146,7 +1278,7 @@
             final int prime = 31;
             int result = 1;
             result = prime * result + line;
-            result = prime * result + source.hashCode();
+            result = prime * result + source.getHashKey().hashCode();
             return result;
         }
 
@@ -1165,7 +1297,7 @@
             if (line != other.line) {
                 return false;
             }
-            return source.equals(other.source);
+            return source.getHashKey().equals(other.source.getHashKey());
         }
 
     }