Scope.java revision 2717:532a67fe69ec
1/*
2 * Copyright (c) 1999, 2014, 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 com.sun.tools.javac.code;
27
28import java.util.*;
29
30import com.sun.tools.javac.util.*;
31import com.sun.tools.javac.util.List;
32
33import static com.sun.tools.javac.code.Scope.LookupKind.NON_RECURSIVE;
34import static com.sun.tools.javac.code.Scope.LookupKind.RECURSIVE;
35
36/** A scope represents an area of visibility in a Java program. The
37 *  Scope class is a container for symbols which provides
38 *  efficient access to symbols given their names. Scopes are implemented
39 *  as hash tables with "open addressing" and "double hashing".
40 *  Scopes can be nested. Nested scopes can share their hash tables.
41 *
42 *  <p><b>This is NOT part of any supported API.
43 *  If you write code that depends on this, you do so at your own risk.
44 *  This code and its internal interfaces are subject to change or
45 *  deletion without notice.</b>
46 */
47public abstract class Scope {
48
49    /** The scope's owner.
50     */
51    public final Symbol owner;
52
53    protected Scope(Symbol owner) {
54        this.owner = owner;
55    }
56
57    /**Returns all Symbols in this Scope. Symbols from outward Scopes are included.
58     */
59    public final Iterable<Symbol> getSymbols() {
60        return getSymbols(noFilter);
61    }
62
63    /**Returns Symbols that match the given filter. Symbols from outward Scopes are included.
64     */
65    public final Iterable<Symbol> getSymbols(Filter<Symbol> sf) {
66        return getSymbols(sf, RECURSIVE);
67    }
68
69    /**Returns all Symbols in this Scope. Symbols from outward Scopes are included
70     * iff lookupKind == RECURSIVE.
71     */
72    public final Iterable<Symbol> getSymbols(LookupKind lookupKind) {
73        return getSymbols(noFilter, lookupKind);
74    }
75
76    /**Returns Symbols that match the given filter. Symbols from outward Scopes are included
77     * iff lookupKind == RECURSIVE.
78     */
79    public abstract Iterable<Symbol> getSymbols(Filter<Symbol> sf, LookupKind lookupKind);
80
81    /**Returns Symbols with the given name. Symbols from outward Scopes are included.
82     */
83    public final Iterable<Symbol> getSymbolsByName(Name name) {
84        return getSymbolsByName(name, RECURSIVE);
85    }
86
87    /**Returns Symbols with the given name that match the given filter.
88     * Symbols from outward Scopes are included.
89     */
90    public final Iterable<Symbol> getSymbolsByName(final Name name, final Filter<Symbol> sf) {
91        return getSymbolsByName(name, sf, RECURSIVE);
92    }
93
94    /**Returns Symbols with the given name. Symbols from outward Scopes are included
95     * iff lookupKind == RECURSIVE.
96     */
97    public final Iterable<Symbol> getSymbolsByName(Name name, LookupKind lookupKind) {
98        return getSymbolsByName(name, noFilter, lookupKind);
99    }
100
101    /**Returns Symbols with the given name that match the given filter.
102     * Symbols from outward Scopes are included iff lookupKind == RECURSIVE.
103     */
104    public abstract Iterable<Symbol> getSymbolsByName(final Name name, final Filter<Symbol> sf,
105            final LookupKind lookupKind);
106
107    /** Return the first Symbol from this or outward scopes with the given name.
108     * Returns null if none.
109     */
110    public final Symbol findFirst(Name name) {
111        return findFirst(name, noFilter);
112    }
113
114    /** Return the first Symbol from this or outward scopes with the given name that matches the
115     *  given filter. Returns null if none.
116     */
117    public Symbol findFirst(Name name, Filter<Symbol> sf) {
118        Iterator<Symbol> it = getSymbolsByName(name, sf).iterator();
119        return it.hasNext() ? it.next() : null;
120    }
121
122    /** Returns true iff there are is at least one Symbol in this scope matching the given filter.
123     *  Does not inspect outward scopes.
124     */
125    public boolean anyMatch(Filter<Symbol> filter) {
126        return getSymbols(filter, NON_RECURSIVE).iterator().hasNext();
127    }
128
129    /** Returns true iff the given Symbol is in this scope or any outward scope.
130     */
131    public boolean includes(final Symbol sym) {
132        return getSymbolsByName(sym.name, new Filter<Symbol>() {
133            @Override
134            public boolean accepts(Symbol t) {
135                return t == sym;
136            }
137        }).iterator().hasNext();
138    }
139
140    /** Returns true iff this scope does not contain any Symbol. Does not inspect outward scopes.
141     */
142    public boolean isEmpty() {
143        return !getSymbols(NON_RECURSIVE).iterator().hasNext();
144    }
145
146    /** Returns the Scope from which the givins Symbol originates in this scope.
147     */
148    public abstract Scope getOrigin(Symbol byName);
149
150    /** Returns true iff the given Symbol is part of this scope due to a static import.
151     */
152    public abstract boolean isStaticallyImported(Symbol byName);
153
154    private static final Filter<Symbol> noFilter = null;
155
156    /** A list of scopes to be notified if items are to be removed from this scope.
157     */
158    List<ScopeListener> listeners = List.nil();
159
160    public void addScopeListener(ScopeListener sl) {
161        listeners = listeners.prepend(sl);
162    }
163
164    public interface ScopeListener {
165        public void symbolAdded(Symbol sym, Scope s);
166        public void symbolRemoved(Symbol sym, Scope s);
167    }
168
169    public enum LookupKind {
170        RECURSIVE,
171        NON_RECURSIVE;
172    }
173
174    /**A scope into which Symbols can be added.*/
175    public abstract static class WriteableScope extends Scope {
176
177        public WriteableScope(Symbol owner) {
178            super(owner);
179        }
180
181        /** Enter the given Symbol into this scope.
182         */
183        public abstract void enter(Symbol c);
184        /** Enter symbol sym in this scope if not already there.
185         */
186        public abstract void enterIfAbsent(Symbol c);
187
188        public abstract void remove(Symbol c);
189
190        /** Construct a fresh scope within this scope, with same owner. The new scope may
191         *  shares internal structures with the this scope. Used in connection with
192         *  method leave if scope access is stack-like in order to avoid allocation
193         *  of fresh tables.
194         */
195        public final WriteableScope dup() {
196            return dup(this.owner);
197        }
198
199        /** Construct a fresh scope within this scope, with new owner. The new scope may
200         *  shares internal structures with the this scope. Used in connection with
201         *  method leave if scope access is stack-like in order to avoid allocation
202         *  of fresh tables.
203         */
204        public abstract WriteableScope dup(Symbol newOwner);
205
206        /** Must be called on dup-ed scopes to be able to work with the outward scope again.
207         */
208        public abstract WriteableScope leave();
209
210        /** Construct a fresh scope within this scope, with same owner. The new scope
211         *  will not share internal structures with this scope.
212         */
213        public final WriteableScope dupUnshared() {
214            return dupUnshared(owner);
215        }
216
217        /** Construct a fresh scope within this scope, with new owner. The new scope
218         *  will not share internal structures with this scope.
219         */
220        public abstract WriteableScope dupUnshared(Symbol newOwner);
221
222        /** Create a new WriteableScope.
223         */
224        public static WriteableScope create(Symbol owner) {
225            return new ScopeImpl(owner);
226        }
227
228    }
229
230    private static class ScopeImpl extends WriteableScope {
231        /** The number of scopes that share this scope's hash table.
232         */
233        private int shared;
234
235        /** Next enclosing scope (with whom this scope may share a hashtable)
236         */
237        public ScopeImpl next;
238
239        /** A hash table for the scope's entries.
240         */
241        Entry[] table;
242
243        /** Mask for hash codes, always equal to (table.length - 1).
244         */
245        int hashMask;
246
247        /** A linear list that also contains all entries in
248         *  reverse order of appearance (i.e later entries are pushed on top).
249         */
250        public Entry elems;
251
252        /** The number of elements in this scope.
253         * This includes deleted elements, whose value is the sentinel.
254         */
255        int nelems = 0;
256
257        /** Use as a "not-found" result for lookup.
258         * Also used to mark deleted entries in the table.
259         */
260        private static final Entry sentinel = new Entry(null, null, null, null);
261
262        /** The hash table's initial size.
263         */
264        private static final int INITIAL_SIZE = 0x10;
265
266        /** Construct a new scope, within scope next, with given owner, using
267         *  given table. The table's length must be an exponent of 2.
268         */
269        private ScopeImpl(ScopeImpl next, Symbol owner, Entry[] table) {
270            super(owner);
271            this.next = next;
272            Assert.check(owner != null);
273            this.table = table;
274            this.hashMask = table.length - 1;
275        }
276
277        /** Convenience constructor used for dup and dupUnshared. */
278        private ScopeImpl(ScopeImpl next, Symbol owner, Entry[] table, int nelems) {
279            this(next, owner, table);
280            this.nelems = nelems;
281        }
282
283        /** Construct a new scope, within scope next, with given owner,
284         *  using a fresh table of length INITIAL_SIZE.
285         */
286        public ScopeImpl(Symbol owner) {
287            this(null, owner, new Entry[INITIAL_SIZE]);
288        }
289
290        /** Construct a fresh scope within this scope, with new owner,
291         *  which shares its table with the outer scope. Used in connection with
292         *  method leave if scope access is stack-like in order to avoid allocation
293         *  of fresh tables.
294         */
295        public WriteableScope dup(Symbol newOwner) {
296            ScopeImpl result = new ScopeImpl(this, newOwner, this.table, this.nelems);
297            shared++;
298            // System.out.println("====> duping scope " + this.hashCode() + " owned by " + newOwner + " to " + result.hashCode());
299            // new Error().printStackTrace(System.out);
300            return result;
301        }
302
303        /** Construct a fresh scope within this scope, with new owner,
304         *  with a new hash table, whose contents initially are those of
305         *  the table of its outer scope.
306         */
307        public WriteableScope dupUnshared(Symbol newOwner) {
308            if (shared > 0) {
309                //The nested Scopes might have already added something to the table, so all items
310                //that don't originate in this Scope or any of its outer Scopes need to be cleared:
311                Set<Scope> acceptScopes = Collections.newSetFromMap(new IdentityHashMap<>());
312                ScopeImpl c = this;
313                while (c != null) {
314                    acceptScopes.add(c);
315                    c = c.next;
316                }
317                int n = 0;
318                Entry[] oldTable = this.table;
319                Entry[] newTable = new Entry[this.table.length];
320                for (int i = 0; i < oldTable.length; i++) {
321                    Entry e = oldTable[i];
322                    while (e != null && e != sentinel && !acceptScopes.contains(e.scope)) {
323                        e = e.shadowed;
324                    }
325                    if (e != null) {
326                        n++;
327                        newTable[i] = e;
328                    }
329                }
330                return new ScopeImpl(this, newOwner, newTable, n);
331            } else {
332                return new ScopeImpl(this, newOwner, this.table.clone(), this.nelems);
333            }
334        }
335
336        /** Remove all entries of this scope from its table, if shared
337         *  with next.
338         */
339        public WriteableScope leave() {
340            Assert.check(shared == 0);
341            if (table != next.table) return next;
342            while (elems != null) {
343                int hash = getIndex(elems.sym.name);
344                Entry e = table[hash];
345                Assert.check(e == elems, elems.sym);
346                table[hash] = elems.shadowed;
347                elems = elems.sibling;
348            }
349            Assert.check(next.shared > 0);
350            next.shared--;
351            next.nelems = nelems;
352            // System.out.println("====> leaving scope " + this.hashCode() + " owned by " + this.owner + " to " + next.hashCode());
353            // new Error().printStackTrace(System.out);
354            return next;
355        }
356
357        /** Double size of hash table.
358         */
359        private void dble() {
360            Assert.check(shared == 0);
361            Entry[] oldtable = table;
362            Entry[] newtable = new Entry[oldtable.length * 2];
363            for (ScopeImpl s = this; s != null; s = s.next) {
364                if (s.table == oldtable) {
365                    Assert.check(s == this || s.shared != 0);
366                    s.table = newtable;
367                    s.hashMask = newtable.length - 1;
368                }
369            }
370            int n = 0;
371            for (int i = oldtable.length; --i >= 0; ) {
372                Entry e = oldtable[i];
373                if (e != null && e != sentinel) {
374                    table[getIndex(e.sym.name)] = e;
375                    n++;
376                }
377            }
378            // We don't need to update nelems for shared inherited scopes,
379            // since that gets handled by leave().
380            nelems = n;
381        }
382
383        /** Enter symbol sym in this scope.
384         */
385        public void enter(Symbol sym) {
386            Assert.check(shared == 0);
387            if (nelems * 3 >= hashMask * 2)
388                dble();
389            int hash = getIndex(sym.name);
390            Entry old = table[hash];
391            if (old == null) {
392                old = sentinel;
393                nelems++;
394            }
395            Entry e = new Entry(sym, old, elems, this);
396            table[hash] = e;
397            elems = e;
398
399            //notify listeners
400            for (List<ScopeListener> l = listeners; l.nonEmpty(); l = l.tail) {
401                l.head.symbolAdded(sym, this);
402            }
403        }
404
405        /** Remove symbol from this scope.  Used when an inner class
406         *  attribute tells us that the class isn't a package member.
407         */
408        public void remove(Symbol sym) {
409            Assert.check(shared == 0);
410            Entry e = lookup(sym.name);
411            if (e.scope == null) return;
412
413            // remove e from table and shadowed list;
414            int i = getIndex(sym.name);
415            Entry te = table[i];
416            if (te == e)
417                table[i] = e.shadowed;
418            else while (true) {
419                if (te.shadowed == e) {
420                    te.shadowed = e.shadowed;
421                    break;
422                }
423                te = te.shadowed;
424            }
425
426            // remove e from elems and sibling list
427            te = elems;
428            if (te == e)
429                elems = e.sibling;
430            else while (true) {
431                if (te.sibling == e) {
432                    te.sibling = e.sibling;
433                    break;
434                }
435                te = te.sibling;
436            }
437
438            //notify listeners
439            for (List<ScopeListener> l = listeners; l.nonEmpty(); l = l.tail) {
440                l.head.symbolRemoved(sym, this);
441            }
442        }
443
444        /** Enter symbol sym in this scope if not already there.
445         */
446        public void enterIfAbsent(Symbol sym) {
447            Assert.check(shared == 0);
448            Entry e = lookup(sym.name);
449            while (e.scope == this && e.sym.kind != sym.kind) e = e.next();
450            if (e.scope != this) enter(sym);
451        }
452
453        /** Given a class, is there already a class with same fully
454         *  qualified name in this (import) scope?
455         */
456        public boolean includes(Symbol c) {
457            for (Scope.Entry e = lookup(c.name);
458                 e.scope == this;
459                 e = e.next()) {
460                if (e.sym == c) return true;
461            }
462            return false;
463        }
464
465        /** Return the entry associated with given name, starting in
466         *  this scope and proceeding outwards. If no entry was found,
467         *  return the sentinel, which is characterized by having a null in
468         *  both its scope and sym fields, whereas both fields are non-null
469         *  for regular entries.
470         */
471        protected Entry lookup(Name name) {
472            return lookup(name, noFilter);
473        }
474
475        protected Entry lookup(Name name, Filter<Symbol> sf) {
476            Entry e = table[getIndex(name)];
477            if (e == null || e == sentinel)
478                return sentinel;
479            while (e.scope != null && (e.sym.name != name || (sf != null && !sf.accepts(e.sym))))
480                e = e.shadowed;
481            return e;
482        }
483
484        public Symbol findFirst(Name name, Filter<Symbol> sf) {
485            return lookup(name, sf).sym;
486        }
487
488        /*void dump (java.io.PrintStream out) {
489            out.println(this);
490            for (int l=0; l < table.length; l++) {
491                Entry le = table[l];
492                out.print("#"+l+": ");
493                if (le==sentinel) out.println("sentinel");
494                else if(le == null) out.println("null");
495                else out.println(""+le+" s:"+le.sym);
496            }
497        }*/
498
499        /** Look for slot in the table.
500         *  We use open addressing with double hashing.
501         */
502        int getIndex (Name name) {
503            int h = name.hashCode();
504            int i = h & hashMask;
505            // The expression below is always odd, so it is guaranteed
506            // to be mutually prime with table.length, a power of 2.
507            int x = hashMask - ((h + (h >> 16)) << 1);
508            int d = -1; // Index of a deleted item.
509            for (;;) {
510                Entry e = table[i];
511                if (e == null)
512                    return d >= 0 ? d : i;
513                if (e == sentinel) {
514                    // We have to keep searching even if we see a deleted item.
515                    // However, remember the index in case we fail to find the name.
516                    if (d < 0)
517                        d = i;
518                } else if (e.sym.name == name)
519                    return i;
520                i = (i + x) & hashMask;
521            }
522        }
523
524        public boolean anyMatch(Filter<Symbol> sf) {
525            return getSymbols(sf, NON_RECURSIVE).iterator().hasNext();
526        }
527
528        public Iterable<Symbol> getSymbols(final Filter<Symbol> sf,
529                                           final LookupKind lookupKind) {
530            return new Iterable<Symbol>() {
531                public Iterator<Symbol> iterator() {
532                    return new Iterator<Symbol>() {
533                        private ScopeImpl currScope = ScopeImpl.this;
534                        private Scope.Entry currEntry = elems;
535                        {
536                            update();
537                        }
538
539                        public boolean hasNext() {
540                            return currEntry != null;
541                        }
542
543                        public Symbol next() {
544                            Symbol sym = (currEntry == null ? null : currEntry.sym);
545                            if (currEntry != null) {
546                                currEntry = currEntry.sibling;
547                            }
548                            update();
549                            return sym;
550                        }
551
552                        public void remove() {
553                            throw new UnsupportedOperationException();
554                        }
555
556                        private void update() {
557                            skipToNextMatchingEntry();
558                            if (lookupKind == RECURSIVE) {
559                                while (currEntry == null && currScope.next != null) {
560                                    currScope = currScope.next;
561                                    currEntry = currScope.elems;
562                                    skipToNextMatchingEntry();
563                                }
564                            }
565                        }
566
567                        void skipToNextMatchingEntry() {
568                            while (currEntry != null && sf != null && !sf.accepts(currEntry.sym)) {
569                                currEntry = currEntry.sibling;
570                            }
571                        }
572                    };
573                }
574            };
575        }
576
577        public Iterable<Symbol> getSymbolsByName(final Name name,
578                                                 final Filter<Symbol> sf,
579                                                 final LookupKind lookupKind) {
580            return new Iterable<Symbol>() {
581                public Iterator<Symbol> iterator() {
582                     return new Iterator<Symbol>() {
583                        Scope.Entry currentEntry = lookup(name, sf);
584
585                        public boolean hasNext() {
586                            return currentEntry.scope != null &&
587                                    (lookupKind == RECURSIVE ||
588                                     currentEntry.scope == ScopeImpl.this);
589                        }
590                        public Symbol next() {
591                            Scope.Entry prevEntry = currentEntry;
592                            currentEntry = currentEntry.next(sf);
593                            return prevEntry.sym;
594                        }
595                        public void remove() {
596                            throw new UnsupportedOperationException();
597                        }
598                    };
599                }
600            };
601        }
602
603        public Scope getOrigin(Symbol s) {
604            for (Scope.Entry e = lookup(s.name); e.scope != null ; e = e.next()) {
605                if (e.sym == s) {
606                    return this;
607                }
608            }
609            return null;
610        }
611
612        @Override
613        public boolean isStaticallyImported(Symbol s) {
614            return false;
615        }
616
617        public String toString() {
618            StringBuilder result = new StringBuilder();
619            result.append("Scope[");
620            for (ScopeImpl s = this; s != null ; s = s.next) {
621                if (s != this) result.append(" | ");
622                for (Entry e = s.elems; e != null; e = e.sibling) {
623                    if (e != s.elems) result.append(", ");
624                    result.append(e.sym);
625                }
626            }
627            result.append("]");
628            return result.toString();
629        }
630    }
631
632    /** A class for scope entries.
633     */
634    private static class Entry {
635
636        /** The referenced symbol.
637         *  sym == null   iff   this == sentinel
638         */
639        public Symbol sym;
640
641        /** An entry with the same hash code, or sentinel.
642         */
643        private Entry shadowed;
644
645        /** Next entry in same scope.
646         */
647        public Entry sibling;
648
649        /** The entry's scope.
650         *  scope == null   iff   this == sentinel
651         */
652        public Scope scope;
653
654        public Entry(Symbol sym, Entry shadowed, Entry sibling, Scope scope) {
655            this.sym = sym;
656            this.shadowed = shadowed;
657            this.sibling = sibling;
658            this.scope = scope;
659        }
660
661        /** Return next entry with the same name as this entry, proceeding
662         *  outwards if not found in this scope.
663         */
664        public Entry next() {
665            return shadowed;
666        }
667
668        public Entry next(Filter<Symbol> sf) {
669            if (shadowed.sym == null || sf == null || sf.accepts(shadowed.sym)) return shadowed;
670            else return shadowed.next(sf);
671        }
672
673    }
674
675    public static class NamedImportScope extends CompoundScope {
676
677        public NamedImportScope(Symbol owner, Scope currentFileScope) {
678            super(owner);
679            prependSubScope(currentFileScope);
680        }
681
682        public void importByName(Scope delegate, Scope origin, Name name, ImportFilter filter) {
683            appendScope(new FilterImportScope(delegate, origin, name, filter, true));
684        }
685
686        public void importType(Scope delegate, Scope origin, Symbol sym) {
687            appendScope(new SingleEntryScope(delegate.owner, sym, origin));
688        }
689
690        private void appendScope(Scope newScope) {
691            List<Scope> existingScopes = this.subScopes.reverse();
692            subScopes = List.of(existingScopes.head);
693            subScopes = subScopes.prepend(newScope);
694            for (Scope s : existingScopes.tail) {
695                subScopes = subScopes.prepend(s);
696            }
697        }
698
699        private static class SingleEntryScope extends Scope {
700
701            private final Symbol sym;
702            private final List<Symbol> content;
703            private final Scope origin;
704
705            public SingleEntryScope(Symbol owner, Symbol sym, Scope origin) {
706                super(owner);
707                this.sym = sym;
708                this.content = List.of(sym);
709                this.origin = origin;
710            }
711
712            @Override
713            public Iterable<Symbol> getSymbols(Filter<Symbol> sf, LookupKind lookupKind) {
714                return sf == null || sf.accepts(sym) ? content : Collections.<Symbol>emptyList();
715            }
716
717            @Override
718            public Iterable<Symbol> getSymbolsByName(Name name,
719                                                     Filter<Symbol> sf,
720                                                     LookupKind lookupKind) {
721                return sym.name == name &&
722                       (sf == null || sf.accepts(sym)) ? content : Collections.<Symbol>emptyList();
723            }
724
725            @Override
726            public Scope getOrigin(Symbol byName) {
727                return sym == byName ? origin : null;
728            }
729
730            @Override
731            public boolean isStaticallyImported(Symbol byName) {
732                return false;
733            }
734
735        }
736    }
737
738    public static class StarImportScope extends CompoundScope {
739
740        public StarImportScope(Symbol owner) {
741            super(owner);
742        }
743
744        public void importAll(Scope delegate,
745                              Scope origin,
746                              ImportFilter filter,
747                              boolean staticImport) {
748            for (Scope existing : subScopes) {
749                Assert.check(existing instanceof FilterImportScope);
750                FilterImportScope fis = (FilterImportScope) existing;
751                if (fis.delegate == delegate && fis.origin == origin &&
752                    fis.filter == filter && fis.staticImport == staticImport)
753                    return ; //avoid entering the same scope twice
754            }
755            prependSubScope(new FilterImportScope(delegate, origin, null, filter, staticImport));
756        }
757
758    }
759
760    public interface ImportFilter {
761        public boolean accepts(Scope origin, Symbol sym);
762    }
763
764    private static class FilterImportScope extends Scope {
765
766        private final Scope delegate;
767        private final Scope origin;
768        private final Name  filterName;
769        private final ImportFilter filter;
770        private final boolean staticImport;
771
772        public FilterImportScope(Scope delegate,
773                                 Scope origin,
774                                 Name  filterName,
775                                 ImportFilter filter,
776                                 boolean staticImport) {
777            super(delegate.owner);
778            this.delegate = delegate;
779            this.origin = origin;
780            this.filterName = filterName;
781            this.filter = filter;
782            this.staticImport = staticImport;
783        }
784
785        @Override
786        public Iterable<Symbol> getSymbols(Filter<Symbol> sf, LookupKind lookupKind) {
787            if (filterName != null)
788                return getSymbolsByName(filterName, sf, lookupKind);
789            return new FilteredIterable(delegate.getSymbols(sf, lookupKind));
790        }
791
792        @Override
793        public Iterable<Symbol> getSymbolsByName(Name name,
794                                                 Filter<Symbol> sf,
795                                                 LookupKind lookupKind) {
796            if (filterName != null && filterName != name)
797                return Collections.emptyList();
798            return new FilteredIterable(delegate.getSymbolsByName(name, sf, lookupKind));
799        }
800
801        @Override
802        public Scope getOrigin(Symbol byName) {
803            return origin;
804        }
805
806        @Override
807        public boolean isStaticallyImported(Symbol byName) {
808            return staticImport;
809        }
810
811        private class FilteredIterator implements Iterator<Symbol> {
812            private final Iterator<Symbol> delegate;
813            private Symbol next;
814
815            public FilteredIterator(Iterator<Symbol> delegate) {
816                this.delegate = delegate;
817                update();
818            }
819
820            void update() {
821                while (delegate.hasNext()) {
822                    if (filter.accepts(origin, next = delegate.next()))
823                        return;
824                }
825
826                next = null;
827            }
828
829            @Override
830            public boolean hasNext() {
831                return next != null;
832            }
833
834            @Override
835            public Symbol next() {
836                Symbol result = next;
837
838                update();
839
840                return result;
841            }
842
843            @Override
844            public void remove() {
845                throw new UnsupportedOperationException("Not supported.");
846            }
847
848        }
849
850        private class FilteredIterable implements Iterable<Symbol> {
851
852            private final Iterable<Symbol> unfiltered;
853
854            public FilteredIterable(Iterable<Symbol> unfiltered) {
855                this.unfiltered = unfiltered;
856            }
857
858            @Override
859            public Iterator<Symbol> iterator() {
860                return new FilteredIterator(unfiltered.iterator());
861            }
862        }
863
864    }
865
866    /** A class scope adds capabilities to keep track of changes in related
867     *  class scopes - this allows client to realize whether a class scope
868     *  has changed, either directly (because a new member has been added/removed
869     *  to this scope) or indirectly (i.e. because a new member has been
870     *  added/removed into a supertype scope)
871     */
872    public static class CompoundScope extends Scope implements ScopeListener {
873
874        List<Scope> subScopes = List.nil();
875        private int mark = 0;
876
877        public CompoundScope(Symbol owner) {
878            super(owner);
879        }
880
881        public void prependSubScope(Scope that) {
882           if (that != null) {
883                subScopes = subScopes.prepend(that);
884                that.addScopeListener(this);
885                mark++;
886                for (ScopeListener sl : listeners) {
887                    sl.symbolAdded(null, this); //propagate upwards in case of nested CompoundScopes
888                }
889           }
890        }
891
892        public void symbolAdded(Symbol sym, Scope s) {
893            mark++;
894            for (ScopeListener sl : listeners) {
895                sl.symbolAdded(sym, s);
896            }
897        }
898
899        public void symbolRemoved(Symbol sym, Scope s) {
900            mark++;
901            for (ScopeListener sl : listeners) {
902                sl.symbolRemoved(sym, s);
903            }
904        }
905
906        public int getMark() {
907            return mark;
908        }
909
910        @Override
911        public String toString() {
912            StringBuilder buf = new StringBuilder();
913            buf.append("CompoundScope{");
914            String sep = "";
915            for (Scope s : subScopes) {
916                buf.append(sep);
917                buf.append(s);
918                sep = ",";
919            }
920            buf.append("}");
921            return buf.toString();
922        }
923
924        @Override
925        public Iterable<Symbol> getSymbols(final Filter<Symbol> sf,
926                                           final LookupKind lookupKind) {
927            return new Iterable<Symbol>() {
928                public Iterator<Symbol> iterator() {
929                    return new CompoundScopeIterator(subScopes) {
930                        Iterator<Symbol> nextIterator(Scope s) {
931                            return s.getSymbols(sf, lookupKind).iterator();
932                        }
933                    };
934                }
935            };
936        }
937
938        @Override
939        public Iterable<Symbol> getSymbolsByName(final Name name,
940                                                 final Filter<Symbol> sf,
941                                                 final LookupKind lookupKind) {
942            return new Iterable<Symbol>() {
943                public Iterator<Symbol> iterator() {
944                    return new CompoundScopeIterator(subScopes) {
945                        Iterator<Symbol> nextIterator(Scope s) {
946                            return s.getSymbolsByName(name, sf, lookupKind).iterator();
947                        }
948                    };
949                }
950            };
951        }
952
953        @Override
954        public Scope getOrigin(Symbol sym) {
955            for (Scope delegate : subScopes) {
956                if (delegate.includes(sym))
957                    return delegate.getOrigin(sym);
958            }
959
960            return null;
961        }
962
963        @Override
964        public boolean isStaticallyImported(Symbol sym) {
965            for (Scope delegate : subScopes) {
966                if (delegate.includes(sym))
967                    return delegate.isStaticallyImported(sym);
968            }
969
970            return false;
971        }
972
973        abstract class CompoundScopeIterator implements Iterator<Symbol> {
974
975            private Iterator<Symbol> currentIterator;
976            private List<Scope> scopesToScan;
977
978            public CompoundScopeIterator(List<Scope> scopesToScan) {
979                this.scopesToScan = scopesToScan;
980                update();
981            }
982
983            abstract Iterator<Symbol> nextIterator(Scope s);
984
985            public boolean hasNext() {
986                return currentIterator != null;
987            }
988
989            public Symbol next() {
990                Symbol sym = currentIterator.next();
991                if (!currentIterator.hasNext()) {
992                    update();
993                }
994                return sym;
995            }
996
997            public void remove() {
998                throw new UnsupportedOperationException();
999            }
1000
1001            private void update() {
1002                while (scopesToScan.nonEmpty()) {
1003                    currentIterator = nextIterator(scopesToScan.head);
1004                    scopesToScan = scopesToScan.tail;
1005                    if (currentIterator.hasNext()) return;
1006                }
1007                currentIterator = null;
1008            }
1009        }
1010    }
1011
1012    /** An error scope, for which the owner should be an error symbol. */
1013    public static class ErrorScope extends ScopeImpl {
1014        ErrorScope(ScopeImpl next, Symbol errSymbol, Entry[] table) {
1015            super(next, /*owner=*/errSymbol, table);
1016        }
1017        public ErrorScope(Symbol errSymbol) {
1018            super(errSymbol);
1019        }
1020        public WriteableScope dup(Symbol newOwner) {
1021            return new ErrorScope(this, newOwner, table);
1022        }
1023        public WriteableScope dupUnshared(Symbol newOwner) {
1024            return new ErrorScope(this, newOwner, table.clone());
1025        }
1026        public Entry lookup(Name name) {
1027            Entry e = super.lookup(name);
1028            if (e.scope == null)
1029                return new Entry(owner, null, null, null);
1030            else
1031                return e;
1032        }
1033    }
1034}
1035