class FilterModelImpl implements FilterModel, Model {

    private final FilterLibraryImpl filterLibraryImpl;
    private final LinkedList<Query> queries;
    private final GraphModel graphModel;
    private final Workspace workspace;
    private final FilterAutoRefreshor autoRefreshor;
    private FilterThread filterThread;
    private Query currentQuery;
    private boolean filtering;
    private boolean selecting;
    private GraphView currentResult;
    private boolean autoRefresh;
    //Listeners
    private List<ChangeListener> listeners;

    public FilterModelImpl(Workspace workspace) {
        this.workspace = workspace;
        filterLibraryImpl = new FilterLibraryImpl(workspace);
        queries = new LinkedList<>();
        listeners = new ArrayList<>();
        autoRefresh = true;

        graphModel = Lookup.getDefault().lookup(GraphController.class).getGraphModel(workspace);
        autoRefreshor = new FilterAutoRefreshor(this, graphModel);
    }

    @Override
    public FilterLibrary getLibrary() {
        return filterLibraryImpl;
    }

    @Override
    public Query[] getQueries() {
        return queries.toArray(new Query[0]);
    }

    public boolean hasQuery(Query query) {
        for (Query q : getQueries()) {
            if (q == query) {
                return true;
            }
        }
        return false;
    }

    public void addFirst(Query function) {
        updateParameters(function);
        queries.addFirst(function);
        currentQuery = function;
        fireChangeEvent();
    }

    public void addLast(Query function) {
        updateParameters(function);
        queries.addLast(function);
        fireChangeEvent();
    }

    public void set(int index, Query function) {
        queries.set(index, function);
    }

    public void remove(Query query) {
        if (query == currentQuery) {
            currentQuery = query.getParent();
        }
        queries.remove(query);
        destroyQuery(query);
        fireChangeEvent();
    }

    public void rename(Query query, String name) {
        query.setName(name);
        fireChangeEvent();
    }

    public void setSubQuery(Query query, Query subQuery) {<FILL_FUNCTION_BODY>}

    public void removeSubQuery(Query query, Query parent) {
        AbstractQueryImpl impl = (AbstractQueryImpl) parent;
        impl.removeSubQuery(query);
        ((AbstractQueryImpl) query).setParent(null);
        if (query == currentQuery) {
            currentQuery = parent;
        }
        fireChangeEvent();
        autoRefreshor.manualRefresh();
    }

    public int getIndex(Query function) {
        int i = 0;
        for (Query f : queries) {
            if (f == function) {
                return i;
            }
            i++;
        }
        return -1;
    }

    @Override
    public boolean isFiltering() {
        return currentQuery != null && filtering;
    }

    public void setFiltering(boolean filtering) {
        this.filtering = filtering;
        if (filtering) {
            this.selecting = false;
        }
    }

    @Override
    public boolean isSelecting() {
        return currentQuery != null && selecting;
    }

    public void setSelecting(boolean selecting) {
        this.selecting = selecting;
        if (selecting) {
            this.filtering = false;
        }
    }

    @Override
    public boolean isAutoRefresh() {
        return autoRefresh;
    }

    public void setAutoRefresh(boolean autoRefresh) {
        this.autoRefresh = autoRefresh;
        fireChangeEvent();
        if (!autoRefresh) {
            autoRefreshor.setEnable(false);
        } else if (autoRefresh && currentResult != null) {
            autoRefreshor.setEnable(true);
        }
    }

    @Override
    public Query getCurrentQuery() {
        return currentQuery;
    }

    public void setCurrentQuery(Query currentQuery) {
        if (currentQuery != null) {
            currentQuery = ((AbstractQueryImpl) currentQuery).getRoot();
        }
        if (this.currentQuery != currentQuery) {
            this.currentQuery = currentQuery;
            fireChangeEvent();
        }
    }

    public void updateParameters(Query query) {
        if (query instanceof FilterQueryImpl) {
            ((FilterQueryImpl) query).updateParameters();
            fireChangeEvent();
        }
    }

    public Query getQuery(Filter filter) {
        for (Query q : getAllQueries()) {
            if (filter == q.getFilter()) {
                return q;
            }
        }
        return null;
    }

    public Query[] getAllQueries() {
        List<Query> result = new ArrayList<>();
        LinkedList<Query> stack = new LinkedList<>();
        stack.addAll(queries);
        while (!stack.isEmpty()) {
            Query q = stack.pop();
            result.add(q);
            stack.addAll(Arrays.asList(q.getChildren()));
        }
        return result.toArray(new Query[0]);
    }

    public FilterThread getFilterThread() {
        return filterThread;
    }

    public void setFilterThread(FilterThread filterThread) {
        this.filterThread = filterThread;
    }

    public FilterAutoRefreshor getAutoRefreshor() {
        return autoRefreshor;
    }

    public GraphView getCurrentResult() {
        return currentResult;
    }

    public void setCurrentResult(GraphView currentResult) {
        this.currentResult = currentResult;
        if (currentResult != null && autoRefresh) {
            autoRefreshor.setEnable(true);
        } else if (currentResult == null && autoRefresh) {
            autoRefreshor.setEnable(false);
        }
    }

    public GraphModel getGraphModel() {
        return graphModel;
    }

    @Override
    public Workspace getWorkspace() {
        return workspace;
    }

    public void destroy() {
        if (filterThread != null) {
            filterThread.setRunning(false);
        }
        autoRefreshor.setRunning(false);
        currentResult = null;
        listeners = null;
        for (Query q : queries) {
            destroyQuery(q);
        }
    }

    private void destroyQuery(Query query) {
        if (query instanceof AbstractQueryImpl) {
            AbstractQueryImpl absQuery = (AbstractQueryImpl) query;
            for (Query q : absQuery.getDescendantsAndSelf()) {
                if (q instanceof FilterQueryImpl) {
                    Filter f = q.getFilter();
                    FilterBuilder builder = q.getBuilder();
                    if (builder != null) {
                        builder.destroy(f);
                    }
                }
            }
        }
    }

    //EVENTS
    @Override
    public void addChangeListener(ChangeListener listener) {
        if (!listeners.contains(listener)) {
            listeners.add(listener);
        }
    }

    @Override
    public void removeChangeListener(ChangeListener listener) {
        if (listeners != null) {
            listeners.remove(listener);
        }
    }

    private void fireChangeEvent() {
        ChangeEvent evt = new ChangeEvent(this);
        for (ChangeListener l : listeners) {
            l.stateChanged(evt);
        }
    }
}

        updateParameters(subQuery);

        //Clean
        queries.remove(subQuery);
        if (subQuery.getParent() != null) {
            ((AbstractQueryImpl) subQuery.getParent()).removeSubQuery(subQuery);
        }
        if (subQuery == currentQuery) {
            currentQuery = ((AbstractQueryImpl) query).getRoot();
        }

        //Set
        AbstractQueryImpl impl = (AbstractQueryImpl) query;
        impl.addSubQuery(subQuery);
        fireChangeEvent();
        autoRefreshor.manualRefresh();
    
<no_super_class>