Scope.java revision 2571:10fc81ac75b4
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            return new ScopeImpl(this, newOwner, this.table.clone(), this.nelems);
309        }
310
311        /** Remove all entries of this scope from its table, if shared
312         *  with next.
313         */
314        public WriteableScope leave() {
315            Assert.check(shared == 0);
316            if (table != next.table) return next;
317            while (elems != null) {
318                int hash = getIndex(elems.sym.name);
319                Entry e = table[hash];
320                Assert.check(e == elems, elems.sym);
321                table[hash] = elems.shadowed;
322                elems = elems.sibling;
323            }
324            Assert.check(next.shared > 0);
325            next.shared--;
326            next.nelems = nelems;
327            // System.out.println("====> leaving scope " + this.hashCode() + " owned by " + this.owner + " to " + next.hashCode());
328            // new Error().printStackTrace(System.out);
329            return next;
330        }
331
332        /** Double size of hash table.
333         */
334        private void dble() {
335            Assert.check(shared == 0);
336            Entry[] oldtable = table;
337            Entry[] newtable = new Entry[oldtable.length * 2];
338            for (ScopeImpl s = this; s != null; s = s.next) {
339                if (s.table == oldtable) {
340                    Assert.check(s == this || s.shared != 0);
341                    s.table = newtable;
342                    s.hashMask = newtable.length - 1;
343                }
344            }
345            int n = 0;
346            for (int i = oldtable.length; --i >= 0; ) {
347                Entry e = oldtable[i];
348                if (e != null && e != sentinel) {
349                    table[getIndex(e.sym.name)] = e;
350                    n++;
351                }
352            }
353            // We don't need to update nelems for shared inherited scopes,
354            // since that gets handled by leave().
355            nelems = n;
356        }
357
358        /** Enter symbol sym in this scope.
359         */
360        public void enter(Symbol sym) {
361            Assert.check(shared == 0);
362            if (nelems * 3 >= hashMask * 2)
363                dble();
364            int hash = getIndex(sym.name);
365            Entry old = table[hash];
366            if (old == null) {
367                old = sentinel;
368                nelems++;
369            }
370            Entry e = new Entry(sym, old, elems, this);
371            table[hash] = e;
372            elems = e;
373
374            //notify listeners
375            for (List<ScopeListener> l = listeners; l.nonEmpty(); l = l.tail) {
376                l.head.symbolAdded(sym, this);
377            }
378        }
379
380        /** Remove symbol from this scope.  Used when an inner class
381         *  attribute tells us that the class isn't a package member.
382         */
383        public void remove(Symbol sym) {
384            Assert.check(shared == 0);
385            Entry e = lookup(sym.name);
386            if (e.scope == null) return;
387
388            // remove e from table and shadowed list;
389            int i = getIndex(sym.name);
390            Entry te = table[i];
391            if (te == e)
392                table[i] = e.shadowed;
393            else while (true) {
394                if (te.shadowed == e) {
395                    te.shadowed = e.shadowed;
396                    break;
397                }
398                te = te.shadowed;
399            }
400
401            // remove e from elems and sibling list
402            te = elems;
403            if (te == e)
404                elems = e.sibling;
405            else while (true) {
406                if (te.sibling == e) {
407                    te.sibling = e.sibling;
408                    break;
409                }
410                te = te.sibling;
411            }
412
413            //notify listeners
414            for (List<ScopeListener> l = listeners; l.nonEmpty(); l = l.tail) {
415                l.head.symbolRemoved(sym, this);
416            }
417        }
418
419        /** Enter symbol sym in this scope if not already there.
420         */
421        public void enterIfAbsent(Symbol sym) {
422            Assert.check(shared == 0);
423            Entry e = lookup(sym.name);
424            while (e.scope == this && e.sym.kind != sym.kind) e = e.next();
425            if (e.scope != this) enter(sym);
426        }
427
428        /** Given a class, is there already a class with same fully
429         *  qualified name in this (import) scope?
430         */
431        public boolean includes(Symbol c) {
432            for (Scope.Entry e = lookup(c.name);
433                 e.scope == this;
434                 e = e.next()) {
435                if (e.sym == c) return true;
436            }
437            return false;
438        }
439
440        /** Return the entry associated with given name, starting in
441         *  this scope and proceeding outwards. If no entry was found,
442         *  return the sentinel, which is characterized by having a null in
443         *  both its scope and sym fields, whereas both fields are non-null
444         *  for regular entries.
445         */
446        protected Entry lookup(Name name) {
447            return lookup(name, noFilter);
448        }
449
450        protected Entry lookup(Name name, Filter<Symbol> sf) {
451            Entry e = table[getIndex(name)];
452            if (e == null || e == sentinel)
453                return sentinel;
454            while (e.scope != null && (e.sym.name != name || (sf != null && !sf.accepts(e.sym))))
455                e = e.shadowed;
456            return e;
457        }
458
459        public Symbol findFirst(Name name, Filter<Symbol> sf) {
460            return lookup(name, sf).sym;
461        }
462
463        /*void dump (java.io.PrintStream out) {
464            out.println(this);
465            for (int l=0; l < table.length; l++) {
466                Entry le = table[l];
467                out.print("#"+l+": ");
468                if (le==sentinel) out.println("sentinel");
469                else if(le == null) out.println("null");
470                else out.println(""+le+" s:"+le.sym);
471            }
472        }*/
473
474        /** Look for slot in the table.
475         *  We use open addressing with double hashing.
476         */
477        int getIndex (Name name) {
478            int h = name.hashCode();
479            int i = h & hashMask;
480            // The expression below is always odd, so it is guaranteed
481            // to be mutually prime with table.length, a power of 2.
482            int x = hashMask - ((h + (h >> 16)) << 1);
483            int d = -1; // Index of a deleted item.
484            for (;;) {
485                Entry e = table[i];
486                if (e == null)
487                    return d >= 0 ? d : i;
488                if (e == sentinel) {
489                    // We have to keep searching even if we see a deleted item.
490                    // However, remember the index in case we fail to find the name.
491                    if (d < 0)
492                        d = i;
493                } else if (e.sym.name == name)
494                    return i;
495                i = (i + x) & hashMask;
496            }
497        }
498
499        public boolean anyMatch(Filter<Symbol> sf) {
500            return getSymbols(sf, NON_RECURSIVE).iterator().hasNext();
501        }
502
503        public Iterable<Symbol> getSymbols(final Filter<Symbol> sf,
504                                           final LookupKind lookupKind) {
505            return new Iterable<Symbol>() {
506                public Iterator<Symbol> iterator() {
507                    return new Iterator<Symbol>() {
508                        private ScopeImpl currScope = ScopeImpl.this;
509                        private Scope.Entry currEntry = elems;
510                        {
511                            update();
512                        }
513
514                        public boolean hasNext() {
515                            return currEntry != null;
516                        }
517
518                        public Symbol next() {
519                            Symbol sym = (currEntry == null ? null : currEntry.sym);
520                            if (currEntry != null) {
521                                currEntry = currEntry.sibling;
522                            }
523                            update();
524                            return sym;
525                        }
526
527                        public void remove() {
528                            throw new UnsupportedOperationException();
529                        }
530
531                        private void update() {
532                            skipToNextMatchingEntry();
533                            if (lookupKind == RECURSIVE) {
534                                while (currEntry == null && currScope.next != null) {
535                                    currScope = currScope.next;
536                                    currEntry = currScope.elems;
537                                    skipToNextMatchingEntry();
538                                }
539                            }
540                        }
541
542                        void skipToNextMatchingEntry() {
543                            while (currEntry != null && sf != null && !sf.accepts(currEntry.sym)) {
544                                currEntry = currEntry.sibling;
545                            }
546                        }
547                    };
548                }
549            };
550        }
551
552        public Iterable<Symbol> getSymbolsByName(final Name name,
553                                                 final Filter<Symbol> sf,
554                                                 final LookupKind lookupKind) {
555            return new Iterable<Symbol>() {
556                public Iterator<Symbol> iterator() {
557                     return new Iterator<Symbol>() {
558                        Scope.Entry currentEntry = lookup(name, sf);
559
560                        public boolean hasNext() {
561                            return currentEntry.scope != null &&
562                                    (lookupKind == RECURSIVE ||
563                                     currentEntry.scope == ScopeImpl.this);
564                        }
565                        public Symbol next() {
566                            Scope.Entry prevEntry = currentEntry;
567                            currentEntry = currentEntry.next(sf);
568                            return prevEntry.sym;
569                        }
570                        public void remove() {
571                            throw new UnsupportedOperationException();
572                        }
573                    };
574                }
575            };
576        }
577
578        public Scope getOrigin(Symbol s) {
579            for (Scope.Entry e = lookup(s.name); e.scope != null ; e = e.next()) {
580                if (e.sym == s) {
581                    return this;
582                }
583            }
584            return null;
585        }
586
587        @Override
588        public boolean isStaticallyImported(Symbol s) {
589            return false;
590        }
591
592        public String toString() {
593            StringBuilder result = new StringBuilder();
594            result.append("Scope[");
595            for (ScopeImpl s = this; s != null ; s = s.next) {
596                if (s != this) result.append(" | ");
597                for (Entry e = s.elems; e != null; e = e.sibling) {
598                    if (e != s.elems) result.append(", ");
599                    result.append(e.sym);
600                }
601            }
602            result.append("]");
603            return result.toString();
604        }
605    }
606
607    /** A class for scope entries.
608     */
609    private static class Entry {
610
611        /** The referenced symbol.
612         *  sym == null   iff   this == sentinel
613         */
614        public Symbol sym;
615
616        /** An entry with the same hash code, or sentinel.
617         */
618        private Entry shadowed;
619
620        /** Next entry in same scope.
621         */
622        public Entry sibling;
623
624        /** The entry's scope.
625         *  scope == null   iff   this == sentinel
626         */
627        public Scope scope;
628
629        public Entry(Symbol sym, Entry shadowed, Entry sibling, Scope scope) {
630            this.sym = sym;
631            this.shadowed = shadowed;
632            this.sibling = sibling;
633            this.scope = scope;
634        }
635
636        /** Return next entry with the same name as this entry, proceeding
637         *  outwards if not found in this scope.
638         */
639        public Entry next() {
640            return shadowed;
641        }
642
643        public Entry next(Filter<Symbol> sf) {
644            if (shadowed.sym == null || sf == null || sf.accepts(shadowed.sym)) return shadowed;
645            else return shadowed.next(sf);
646        }
647
648    }
649
650    public static class NamedImportScope extends CompoundScope {
651
652        public NamedImportScope(Symbol owner, Scope currentFileScope) {
653            super(owner);
654            prependSubScope(currentFileScope);
655        }
656
657        public void importByName(Scope delegate, Scope origin, Name name, ImportFilter filter) {
658            appendScope(new FilterImportScope(delegate, origin, name, filter, true));
659        }
660
661        public void importType(Scope delegate, Scope origin, Symbol sym) {
662            appendScope(new SingleEntryScope(delegate.owner, sym, origin));
663        }
664
665        private void appendScope(Scope newScope) {
666            List<Scope> existingScopes = this.subScopes.reverse();
667            subScopes = List.of(existingScopes.head);
668            subScopes = subScopes.prepend(newScope);
669            for (Scope s : existingScopes.tail) {
670                subScopes = subScopes.prepend(s);
671            }
672        }
673
674        private static class SingleEntryScope extends Scope {
675
676            private final Symbol sym;
677            private final List<Symbol> content;
678            private final Scope origin;
679
680            public SingleEntryScope(Symbol owner, Symbol sym, Scope origin) {
681                super(owner);
682                this.sym = sym;
683                this.content = List.of(sym);
684                this.origin = origin;
685            }
686
687            @Override
688            public Iterable<Symbol> getSymbols(Filter<Symbol> sf, LookupKind lookupKind) {
689                return sf == null || sf.accepts(sym) ? content : Collections.<Symbol>emptyList();
690            }
691
692            @Override
693            public Iterable<Symbol> getSymbolsByName(Name name,
694                                                     Filter<Symbol> sf,
695                                                     LookupKind lookupKind) {
696                return sym.name == name &&
697                       (sf == null || sf.accepts(sym)) ? content : Collections.<Symbol>emptyList();
698            }
699
700            @Override
701            public Scope getOrigin(Symbol byName) {
702                return sym == byName ? origin : null;
703            }
704
705            @Override
706            public boolean isStaticallyImported(Symbol byName) {
707                return false;
708            }
709
710        }
711    }
712
713    public static class StarImportScope extends CompoundScope {
714
715        public StarImportScope(Symbol owner) {
716            super(owner);
717        }
718
719        public void importAll(Scope delegate,
720                              Scope origin,
721                              ImportFilter filter,
722                              boolean staticImport) {
723            for (Scope existing : subScopes) {
724                Assert.check(existing instanceof FilterImportScope);
725                FilterImportScope fis = (FilterImportScope) existing;
726                if (fis.delegate == delegate && fis.origin == origin &&
727                    fis.filter == filter && fis.staticImport == staticImport)
728                    return ; //avoid entering the same scope twice
729            }
730            prependSubScope(new FilterImportScope(delegate, origin, null, filter, staticImport));
731        }
732
733    }
734
735    public interface ImportFilter {
736        public boolean accepts(Scope origin, Symbol sym);
737    }
738
739    private static class FilterImportScope extends Scope {
740
741        private final Scope delegate;
742        private final Scope origin;
743        private final Name  filterName;
744        private final ImportFilter filter;
745        private final boolean staticImport;
746
747        public FilterImportScope(Scope delegate,
748                                 Scope origin,
749                                 Name  filterName,
750                                 ImportFilter filter,
751                                 boolean staticImport) {
752            super(delegate.owner);
753            this.delegate = delegate;
754            this.origin = origin;
755            this.filterName = filterName;
756            this.filter = filter;
757            this.staticImport = staticImport;
758        }
759
760        @Override
761        public Iterable<Symbol> getSymbols(Filter<Symbol> sf, LookupKind lookupKind) {
762            if (filterName != null)
763                return getSymbolsByName(filterName, sf, lookupKind);
764            return new FilteredIterable(delegate.getSymbols(sf, lookupKind));
765        }
766
767        @Override
768        public Iterable<Symbol> getSymbolsByName(Name name,
769                                                 Filter<Symbol> sf,
770                                                 LookupKind lookupKind) {
771            if (filterName != null && filterName != name)
772                return Collections.emptyList();
773            return new FilteredIterable(delegate.getSymbolsByName(name, sf, lookupKind));
774        }
775
776        @Override
777        public Scope getOrigin(Symbol byName) {
778            return origin;
779        }
780
781        @Override
782        public boolean isStaticallyImported(Symbol byName) {
783            return staticImport;
784        }
785
786        private class FilteredIterator implements Iterator<Symbol> {
787            private final Iterator<Symbol> delegate;
788            private Symbol next;
789
790            public FilteredIterator(Iterator<Symbol> delegate) {
791                this.delegate = delegate;
792                update();
793            }
794
795            void update() {
796                while (delegate.hasNext()) {
797                    if (filter.accepts(origin, next = delegate.next()))
798                        return;
799                }
800
801                next = null;
802            }
803
804            @Override
805            public boolean hasNext() {
806                return next != null;
807            }
808
809            @Override
810            public Symbol next() {
811                Symbol result = next;
812
813                update();
814
815                return result;
816            }
817
818            @Override
819            public void remove() {
820                throw new UnsupportedOperationException("Not supported.");
821            }
822
823        }
824
825        private class FilteredIterable implements Iterable<Symbol> {
826
827            private final Iterable<Symbol> unfiltered;
828
829            public FilteredIterable(Iterable<Symbol> unfiltered) {
830                this.unfiltered = unfiltered;
831            }
832
833            @Override
834            public Iterator<Symbol> iterator() {
835                return new FilteredIterator(unfiltered.iterator());
836            }
837        }
838
839    }
840
841    /** A class scope adds capabilities to keep track of changes in related
842     *  class scopes - this allows client to realize whether a class scope
843     *  has changed, either directly (because a new member has been added/removed
844     *  to this scope) or indirectly (i.e. because a new member has been
845     *  added/removed into a supertype scope)
846     */
847    public static class CompoundScope extends Scope implements ScopeListener {
848
849        List<Scope> subScopes = List.nil();
850        private int mark = 0;
851
852        public CompoundScope(Symbol owner) {
853            super(owner);
854        }
855
856        public void prependSubScope(Scope that) {
857           if (that != null) {
858                subScopes = subScopes.prepend(that);
859                that.addScopeListener(this);
860                mark++;
861                for (ScopeListener sl : listeners) {
862                    sl.symbolAdded(null, this); //propagate upwards in case of nested CompoundScopes
863                }
864           }
865        }
866
867        public void symbolAdded(Symbol sym, Scope s) {
868            mark++;
869            for (ScopeListener sl : listeners) {
870                sl.symbolAdded(sym, s);
871            }
872        }
873
874        public void symbolRemoved(Symbol sym, Scope s) {
875            mark++;
876            for (ScopeListener sl : listeners) {
877                sl.symbolRemoved(sym, s);
878            }
879        }
880
881        public int getMark() {
882            return mark;
883        }
884
885        @Override
886        public String toString() {
887            StringBuilder buf = new StringBuilder();
888            buf.append("CompoundScope{");
889            String sep = "";
890            for (Scope s : subScopes) {
891                buf.append(sep);
892                buf.append(s);
893                sep = ",";
894            }
895            buf.append("}");
896            return buf.toString();
897        }
898
899        @Override
900        public Iterable<Symbol> getSymbols(final Filter<Symbol> sf,
901                                           final LookupKind lookupKind) {
902            return new Iterable<Symbol>() {
903                public Iterator<Symbol> iterator() {
904                    return new CompoundScopeIterator(subScopes) {
905                        Iterator<Symbol> nextIterator(Scope s) {
906                            return s.getSymbols(sf, lookupKind).iterator();
907                        }
908                    };
909                }
910            };
911        }
912
913        @Override
914        public Iterable<Symbol> getSymbolsByName(final Name name,
915                                                 final Filter<Symbol> sf,
916                                                 final LookupKind lookupKind) {
917            return new Iterable<Symbol>() {
918                public Iterator<Symbol> iterator() {
919                    return new CompoundScopeIterator(subScopes) {
920                        Iterator<Symbol> nextIterator(Scope s) {
921                            return s.getSymbolsByName(name, sf, lookupKind).iterator();
922                        }
923                    };
924                }
925            };
926        }
927
928        @Override
929        public Scope getOrigin(Symbol sym) {
930            for (Scope delegate : subScopes) {
931                if (delegate.includes(sym))
932                    return delegate.getOrigin(sym);
933            }
934
935            return null;
936        }
937
938        @Override
939        public boolean isStaticallyImported(Symbol sym) {
940            for (Scope delegate : subScopes) {
941                if (delegate.includes(sym))
942                    return delegate.isStaticallyImported(sym);
943            }
944
945            return false;
946        }
947
948        abstract class CompoundScopeIterator implements Iterator<Symbol> {
949
950            private Iterator<Symbol> currentIterator;
951            private List<Scope> scopesToScan;
952
953            public CompoundScopeIterator(List<Scope> scopesToScan) {
954                this.scopesToScan = scopesToScan;
955                update();
956            }
957
958            abstract Iterator<Symbol> nextIterator(Scope s);
959
960            public boolean hasNext() {
961                return currentIterator != null;
962            }
963
964            public Symbol next() {
965                Symbol sym = currentIterator.next();
966                if (!currentIterator.hasNext()) {
967                    update();
968                }
969                return sym;
970            }
971
972            public void remove() {
973                throw new UnsupportedOperationException();
974            }
975
976            private void update() {
977                while (scopesToScan.nonEmpty()) {
978                    currentIterator = nextIterator(scopesToScan.head);
979                    scopesToScan = scopesToScan.tail;
980                    if (currentIterator.hasNext()) return;
981                }
982                currentIterator = null;
983            }
984        }
985    }
986
987    /** An error scope, for which the owner should be an error symbol. */
988    public static class ErrorScope extends ScopeImpl {
989        ErrorScope(ScopeImpl next, Symbol errSymbol, Entry[] table) {
990            super(next, /*owner=*/errSymbol, table);
991        }
992        public ErrorScope(Symbol errSymbol) {
993            super(errSymbol);
994        }
995        public WriteableScope dup(Symbol newOwner) {
996            return new ErrorScope(this, newOwner, table);
997        }
998        public WriteableScope dupUnshared(Symbol newOwner) {
999            return new ErrorScope(this, newOwner, table.clone());
1000        }
1001        public Entry lookup(Name name) {
1002            Entry e = super.lookup(name);
1003            if (e.scope == null)
1004                return new Entry(owner, null, null, null);
1005            else
1006                return e;
1007        }
1008    }
1009}
1010