Wrap.java revision 3827:44bdefe64114
1/*
2 * Copyright (c) 2015, 2016, Oracle and/or its affiliates. All rights reserved.
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * This code is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 only, as
7 * published by the Free Software Foundation.  Oracle designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Oracle in the LICENSE file that accompanied this code.
10 *
11 * This code is distributed in the hope that it will be useful, but WITHOUT
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
14 * version 2 for more details (a copy is included in the LICENSE file that
15 * accompanied this code).
16 *
17 * You should have received a copy of the GNU General Public License version
18 * 2 along with this work; if not, write to the Free Software Foundation,
19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20 *
21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22 * or visit www.oracle.com if you need additional information or have any
23 * questions.
24 */
25
26package jdk.jshell;
27
28import java.util.Arrays;
29import static java.util.stream.Collectors.joining;
30import static jdk.jshell.Util.DOIT_METHOD_NAME;
31
32/**
33 * Wrapping of source into Java methods, fields, etc.  All but outer layer
34 * wrapping with imports and class.
35 *
36 * @author Robert Field
37 */
38abstract class Wrap implements GeneralWrap {
39
40    private static Wrap methodWrap(String prefix, String source, String suffix) {
41        Wrap wunit = new NoWrap(source);
42        return new DoitMethodWrap(new CompoundWrap(prefix, wunit, suffix));
43    }
44
45    public static Wrap methodWrap(String source) {
46        return methodWrap("", source, semi(source) + "        return null;\n");
47    }
48
49    public static Wrap methodReturnWrap(String source) {
50        return methodWrap("return ", source, semi(source));
51    }
52
53    public static Wrap methodUnreachableSemiWrap(String source) {
54        return methodWrap("", source, semi(source));
55    }
56
57    public static Wrap methodUnreachableWrap(String source) {
58        return methodWrap("", source, "");
59    }
60
61    private static String indent(int n) {
62        return "                              ".substring(0, n * 4);
63    }
64
65    private static String nlindent(int n) {
66        return "\n" + indent(n);
67    }
68
69    /**
70     *
71     * @param in
72     * @param rname
73     * @param rinit Initializer or null
74     * @param rdecl Type name and name
75     * @return
76     */
77    public static Wrap varWrap(String source, Range rtype, String brackets, Range rname, Range rinit) {
78        RangeWrap wname = new RangeWrap(source, rname);
79        RangeWrap wtype = new RangeWrap(source, rtype);
80        Wrap wVarDecl = new VarDeclareWrap(wtype, brackets, wname);
81        Wrap wmeth;
82
83        if (rinit == null) {
84            wmeth = new CompoundWrap(new NoWrap(" "), "   return null;\n");
85        } else {
86            RangeWrap winit = new RangeWrap(source, rinit);
87        // int x = y
88            // int x_ = y; return x = x_;
89            // decl + "_ = " + init ; + "return " + name + "=" + name + "_ ;"
90            wmeth = new CompoundWrap(
91                    wtype, brackets + " ", wname, "_ =\n        ", winit, semi(winit),
92                    "        return ", wname, " = ", wname, "_;\n"
93            );
94        }
95        Wrap wInitMeth = new DoitMethodWrap(wmeth);
96        return new CompoundWrap(wVarDecl, wInitMeth);
97    }
98
99    public static Wrap tempVarWrap(String source, String typename, String name) {
100        RangeWrap winit = new NoWrap(source);
101        // y
102        // return $1 = y;
103        // "return " + $1 + "=" + init ;
104        Wrap wmeth = new CompoundWrap("return " + name + " =\n        ", winit, semi(winit));
105        Wrap wInitMeth = new DoitMethodWrap(wmeth);
106
107        String varDecl = "    public static\n    " + typename + " " + name + ";\n";
108        return new CompoundWrap(varDecl, wInitMeth);
109    }
110
111    public static Wrap simpleWrap(String source) {
112        return new NoWrap(source);
113    }
114
115    public static Wrap classMemberWrap(String source) {
116        Wrap w = new NoWrap(source);
117        return new CompoundWrap("    public static\n    ", w);
118    }
119
120    private static int countLines(String s) {
121        return countLines(s, 0, s.length());
122    }
123
124    private static int countLines(String s, int from, int toEx) {
125        int cnt = 0;
126        int idx = from;
127        while ((idx = s.indexOf('\n', idx)) > 0) {
128            if (idx >= toEx) break;
129            ++cnt;
130            ++idx;
131        }
132        return cnt;
133    }
134
135    public static final class Range {
136        final int begin;
137        final int end;
138
139        Range(int begin, int end) {
140            this.begin = begin;
141            this.end = end;
142        }
143
144        Range(String s) {
145            this.begin = 0;
146            this.end = s.length();
147        }
148
149        String part(String s) {
150            return s.substring(begin, end);
151        }
152
153        int length() {
154            return end - begin;
155        }
156
157        boolean isEmpty() {
158            return end == begin;
159        }
160
161        void verify(String s) {
162            if (begin < 0 || end <= begin || end > s.length()) {
163                throw new InternalError("Bad Range: " + s + "[" + begin + "," + end + "]");
164            }
165        }
166
167        @Override
168        public String toString() {
169            return "Range[" + begin + "," + end + "]";
170        }
171    }
172
173    public static class CompoundWrap extends Wrap {
174
175        final Object[] os;
176        final String wrapped;
177        final int snidxFirst;
178        final int snidxLast;
179        final int snlineFirst;
180        final int snlineLast;
181
182        CompoundWrap(Object... os) {
183            this.os = os;
184            int sniFirst = Integer.MAX_VALUE;
185            int sniLast = Integer.MIN_VALUE;
186            int snlnFirst = Integer.MAX_VALUE;
187            int snlnLast = Integer.MIN_VALUE;
188            StringBuilder sb = new StringBuilder();
189            for (Object o : os) {
190                if (o instanceof String) {
191                    String s = (String) o;
192                    sb.append(s);
193                } else if (o instanceof Wrap) {
194                    Wrap w = (Wrap) o;
195                    if (w.firstSnippetIndex() < sniFirst) {
196                        sniFirst = w.firstSnippetIndex();
197                    }
198                    if (w.lastSnippetIndex() > sniLast) {
199                        sniLast = w.lastSnippetIndex();
200                    }
201                    if (w.firstSnippetLine() < snlnFirst) {
202                        snlnFirst = w.firstSnippetLine();
203                    }
204                    if (w.lastSnippetLine() > snlnLast) {
205                        snlnLast = w.lastSnippetLine();
206                    }
207                    sb.append(w.wrapped());
208                } else {
209                    throw new InternalError("Bad object in CommoundWrap: " + o);
210                }
211            }
212            this.wrapped = sb.toString();
213            this.snidxFirst = sniFirst;
214            this.snidxLast = sniLast;
215            this.snlineFirst = snlnFirst;
216            this.snlineLast = snlnLast;
217        }
218
219        @Override
220        public String wrapped() {
221            return wrapped;
222        }
223
224        @Override
225        public int snippetIndexToWrapIndex(int sni) {
226            int before = 0;
227            for (Object o : os) {
228                if (o instanceof String) {
229                    String s = (String) o;
230                    before += s.length();
231                } else if (o instanceof Wrap) {
232                    Wrap w = (Wrap) o;
233                    if (sni >= w.firstSnippetIndex() && sni <= w.lastSnippetIndex()) {
234                        return w.snippetIndexToWrapIndex(sni) + before;
235                    }
236                    before += w.wrapped().length();
237                }
238            }
239            return 0;
240        }
241
242        Wrap wrapIndexToWrap(long wi) {
243            int before = 0;
244            Wrap w = null;
245            for (Object o : os) {
246                if (o instanceof String) {
247                    String s = (String) o;
248                    before += s.length();
249                } else if (o instanceof Wrap) {
250                    w = (Wrap) o;
251                    int len = w.wrapped().length();
252                    if ((wi - before) <= len) {
253                        //System.err.printf("Defer to wrap %s - wi: %d. before; %d   -- %s  >>> %s\n",
254                        //        w, wi, before, w.debugPos(wi - before), w.wrapped());
255                        return w;
256                    }
257                    before += len;
258                }
259            }
260            return w;
261        }
262
263        @Override
264        public int wrapIndexToSnippetIndex(int wi) {
265            int before = 0;
266            for (Object o : os) {
267                if (o instanceof String) {
268                    String s = (String) o;
269                    before += s.length();
270                } else if (o instanceof Wrap) {
271                    Wrap w = (Wrap) o;
272                    int len = w.wrapped().length();
273                    if ((wi - before) <= len) {
274                        //System.err.printf("Defer to wrap %s - wi: %d. before; %d   -- %s  >>> %s\n",
275                        //        w, wi, before, w.debugPos(wi - before), w.wrapped());
276                        return w.wrapIndexToSnippetIndex(wi - before);
277                    }
278                    before += len;
279                }
280            }
281            return lastSnippetIndex();
282        }
283
284        @Override
285        public int firstSnippetIndex() {
286            return snidxFirst;
287        }
288
289        @Override
290        public int lastSnippetIndex() {
291            return snidxLast;
292        }
293
294        @Override
295        public int snippetLineToWrapLine(int snline) {
296            int before = 0;
297            for (Object o : os) {
298                if (o instanceof String) {
299                    String s = (String) o;
300                    before += countLines(s);
301                } else if (o instanceof Wrap) {
302                    Wrap w = (Wrap) o;
303                    if (snline >= w.firstSnippetLine() && snline <= w.lastSnippetLine()) {
304                        return w.snippetLineToWrapLine(snline) + before;
305                    }
306                    before += countLines(w.wrapped());
307                }
308            }
309            return 0;
310        }
311
312        Wrap wrapLineToWrap(int wline) {
313            int before = 0;
314            Wrap w = null;
315            for (Object o : os) {
316                if (o instanceof String) {
317                    String s = (String) o;
318                    before += countLines(s);
319                } else if (o instanceof Wrap) {
320                    w = (Wrap) o;
321                    int lns = countLines(w.wrapped());
322                    if ((wline - before) < lns) {
323                        return w;
324                    }
325                    before += lns;
326                }
327            }
328            return w;
329        }
330
331        @Override
332        public int wrapLineToSnippetLine(int wline) {
333            int before = 0;
334            for (Object o : os) {
335                if (o instanceof String) {
336                    String s = (String) o;
337                    before += countLines(s);
338                } else if (o instanceof Wrap) {
339                    Wrap w = (Wrap) o;
340                    int lns = countLines(w.wrapped());
341                    if ((wline - before) < lns) {
342                        return w.wrapLineToSnippetLine(wline - before);
343                    }
344                    before += lns;
345                }
346            }
347            return 0;
348        }
349
350        @Override
351        public int firstSnippetLine() {
352            return snlineFirst;
353        }
354
355        @Override
356        public int lastSnippetLine() {
357            return snlineLast;
358        }
359
360        @Override
361        public String toString() {
362            return "CompoundWrap(" + Arrays.stream(os).map(Object::toString).collect(joining(",")) + ")";
363        }
364    }
365
366    private static class RangeWrap extends Wrap {
367
368        final Range range;
369        final String wrapped;
370        final int firstSnline;
371        final int lastSnline;
372
373        RangeWrap(String snippetSource, Range usedWithinSnippet) {
374            this.range = usedWithinSnippet;
375            this.wrapped = usedWithinSnippet.part(snippetSource);
376            usedWithinSnippet.verify(snippetSource);
377            this.firstSnline = countLines(snippetSource, 0, range.begin);
378            this.lastSnline = firstSnline + countLines(snippetSource, range.begin, range.end);
379        }
380
381        @Override
382        public String wrapped() {
383            return wrapped;
384        }
385
386        @Override
387        public int snippetIndexToWrapIndex(int sni) {
388            if (sni < range.begin) {
389                return 0;
390            }
391            if (sni > range.end) {
392                return range.length();
393            }
394            return sni - range.begin;
395        }
396
397        @Override
398        public int wrapIndexToSnippetIndex(int wi) {
399            if (wi < 0) {
400                return 0; // bad index
401            }
402            int max = range.length();
403            if (wi > max) {
404                wi = max;
405            }
406            return wi + range.begin;
407        }
408
409        @Override
410        public int firstSnippetIndex() {
411            return range.begin;
412        }
413
414        @Override
415        public int lastSnippetIndex() {
416            return range.end;
417        }
418
419        @Override
420        public int snippetLineToWrapLine(int snline) {
421            if (snline < firstSnline) {
422                return 0;
423            }
424            if (snline >= lastSnline) {
425                return lastSnline - firstSnline;
426            }
427            return snline - firstSnline;
428        }
429
430        @Override
431        public int wrapLineToSnippetLine(int wline) {
432            if (wline < 0) {
433                return 0; // bad index
434            }
435            int max = lastSnline - firstSnline;
436            if (wline > max) {
437                wline = max;
438            }
439            return wline + firstSnline;
440        }
441
442        @Override
443        public int firstSnippetLine() {
444            return firstSnline;
445        }
446
447        @Override
448        public int lastSnippetLine() {
449            return lastSnline;
450        }
451
452        @Override
453        public String toString() {
454            return "RangeWrap(" + range + ")";
455        }
456    }
457
458    private static class NoWrap extends RangeWrap {
459
460        NoWrap(String unit) {
461            super(unit, new Range(unit));
462        }
463    }
464
465    private static String semi(Wrap w) {
466        return semi(w.wrapped());
467    }
468
469    private static String semi(String s) {
470        return ((s.endsWith(";")) ? "\n" : ((s.endsWith(";\n")) ? "" : ";\n"));
471    }
472
473    private static class DoitMethodWrap extends CompoundWrap {
474
475        DoitMethodWrap(Wrap w) {
476            super("    public static Object " + DOIT_METHOD_NAME + "() throws Throwable {\n"
477                    + "        ", w,
478                    "    }\n");
479        }
480    }
481
482    private static class VarDeclareWrap extends CompoundWrap {
483
484        VarDeclareWrap(Wrap wtype, String brackets, Wrap wname) {
485            super("    public static ", wtype, brackets + " ", wname, semi(wname));
486        }
487    }
488}
489