class TableIdentsExtractor {

    private static final TableIdentRelationVisitor RELATION_TABLE_IDENT_EXTRACTOR = new TableIdentRelationVisitor();
    private static final TableIdentSymbolVisitor SYMBOL_TABLE_IDENT_EXTRACTOR = new TableIdentSymbolVisitor();

    /**
     * Extracts all table idents from all given symbols if possible (some symbols don't provide any table ident info)
     */
    public static Collection<RelationName> extract(Iterable<? extends Symbol> symbols) {
        Collection<RelationName> relationNames = new HashSet<>();
        for (Symbol symbol : symbols) {
            symbol.accept(SYMBOL_TABLE_IDENT_EXTRACTOR, relationNames);
        }
        return relationNames;
    }

    /**
     * Extracts all table idents from the given symbol if possible (some symbols don't provide any table ident info)
     */
    public static Iterable<RelationName> extract(Symbol symbol) {<FILL_FUNCTION_BODY>}

    private static class TableIdentSymbolVisitor extends DefaultTraversalSymbolVisitor<Collection<RelationName>, Void> {

        @Override
        protected Void visitSymbol(Symbol symbol, Collection<RelationName> context) {
            throw new IllegalStateException(String.format(Locale.ENGLISH,
                "Symbol '%s' not supported", Symbol.class.getName()));
        }

        @Override
        public Void visitAggregation(Aggregation symbol, Collection<RelationName> context) {
            context.addAll(extract(symbol.inputs()));
            return null;
        }

        @Override
        public Void visitReference(Reference symbol, Collection<RelationName> context) {
            context.add(symbol.ident().tableIdent());
            return null;
        }

        @Override
        public Void visitDynamicReference(DynamicReference symbol, Collection<RelationName> context) {
            return visitReference(symbol, context);
        }

        @Override
        public Void visitFunction(Function symbol, Collection<RelationName> context) {
            return null;
        }

        @Override
        public Void visitLiteral(Literal<?> symbol, Collection<RelationName> context) {
            return null;
        }

        @Override
        public Void visitInputColumn(InputColumn inputColumn, Collection<RelationName> context) {
            return null;
        }

        @Override
        public Void visitField(ScopedSymbol field, Collection<RelationName> context) {
            context.add(field.relation());
            return null;
        }

        @Override
        public Void visitFetchReference(FetchReference fetchReference, Collection<RelationName> context) {
            return ((Symbol) fetchReference.ref()).accept(this, context);
        }

        @Override
        public Void visitParameterSymbol(ParameterSymbol parameterSymbol, Collection<RelationName> context) {
            return null;
        }

        @Override
        public Void visitSelectSymbol(SelectSymbol selectSymbol, Collection<RelationName> context) {
            return selectSymbol.relation().accept(RELATION_TABLE_IDENT_EXTRACTOR, context);
        }
    }

    private static class TableIdentRelationVisitor extends AnalyzedRelationVisitor<Collection<RelationName>, Void> {

        @Override
        protected Void visitAnalyzedRelation(AnalyzedRelation relation, Collection<RelationName> context) {
            throw new IllegalStateException(String.format(Locale.ENGLISH,
                "AnalyzedRelation '%s' not supported", relation.getClass()));
        }

        @Override
        public Void visitTableRelation(TableRelation tableRelation, Collection<RelationName> context) {
            context.add(tableRelation.tableInfo().ident());
            return null;
        }

        @Override
        public Void visitDocTableRelation(DocTableRelation relation, Collection<RelationName> context) {
            context.add(relation.tableInfo().ident());
            return null;
        }

        @Override
        public Void visitTableFunctionRelation(TableFunctionRelation tableFunctionRelation, Collection<RelationName> context) {
            return null;
        }

        @Override
        public Void visitQueriedSelectRelation(QueriedSelectRelation relation, Collection<RelationName> context) {
            for (AnalyzedRelation analyzedRelation : relation.from()) {
                analyzedRelation.accept(this, context);
            }
            return null;
        }
    }
}

        Set<RelationName> relationNames = new HashSet<>();
        symbol.accept(SYMBOL_TABLE_IDENT_EXTRACTOR, relationNames);
        return relationNames;
    
<no_super_class>