class FunctionImpl implements Function {

    protected final AppearanceModelImpl model;
    protected final Class<? extends Element> elementClass;
    protected final String name;
    protected final Column column;
    protected final Transformer transformer;
    protected final TransformerUI transformerUI;
    protected final PartitionImpl partition;
    protected final RankingImpl ranking;
    protected final AtomicInteger version;
    // Version
    protected WeakReference<Graph> lastGraph;
    protected boolean lastTransformNullValues;

    protected FunctionImpl(AppearanceModelImpl model, String name, Class<? extends Element> elementClass, Column column,
                           Transformer transformer, TransformerUI transformerUI, PartitionImpl partition,
                           RankingImpl ranking) {
        if (name == null) {
            throw new NullPointerException("The name can't be null");
        }
        this.model = model;
        this.name = name;
        this.elementClass = elementClass;
        this.column = column;
        try {
            this.transformer = transformer.getClass().newInstance();
        } catch (Exception ex) {
            throw new RuntimeException(ex);
        }
        this.transformerUI = transformerUI;
        this.partition = partition;
        this.ranking = ranking;
        this.version =
            new AtomicInteger(partition != null ? partition.getVersion(model.getPartitionGraph()) : Integer.MIN_VALUE);
        this.lastGraph = partition != null ? new WeakReference<>(model.getPartitionGraph()) : null;
        this.lastTransformNullValues = model.isTransformNullValues();
    }

    @Override
    public void transform(Element element) {
        Graph graph = getGraph();
        if (isSimple()) {
            ((SimpleTransformer) transformer).transform(element);
        } else if (isRanking()) {
            transformRanking(element, graph, ranking.getMinValue(graph), ranking.getMaxValue(graph));
        } else if (isPartition()) {
            transformPartition(element, graph);
        }
    }

    @Override
    public void transformAll(Iterable<? extends Element> elementIterable) {
        Graph graph = getGraph();
        if (!graph.getView().isDestroyed()) {
            if (isSimple()) {
                elementIterable.forEach(((SimpleTransformer) transformer)::transform);
            } else if (isRanking()) {
                final Number minValue = ranking.getMinValue(graph);
                final Number maxValue = ranking.getMaxValue(graph);
                elementIterable.forEach(e -> transformRanking(e, graph, minValue, maxValue));
            } else if (isPartition()) {
                elementIterable.forEach(e -> transformPartition(e, graph));
            }
        }
    }

    private void transformPartition(Element element, Graph graph) {
        Object val = partition.getValue(element, graph);
        if (val != null || model.isTransformNullValues()) {
            ((PartitionTransformer) transformer).transform(element, partition, val);
        }
    }

    private void transformRanking(Element element, Graph graph, Number minValue, Number maxValue) {
        // Always use visible graph to get the value (see #2629)
        Number val = ranking.getValue(element, graph.getModel().getGraphVisible());
        if (val != null) {
            float normalizedValue = ranking.normalize(val, ranking.getInterpolator(), minValue, maxValue);
            ((RankingTransformer) transformer).transform(element, ranking, val, normalizedValue);
        } else if (model.isTransformNullValues()) {
            ((RankingTransformer) transformer).transform(element, ranking, null, 0f);
        }
    }

    public boolean hasChanged() {
        if (isPartition()) {
            Graph graph = model.getPartitionGraph();

            // Check if view has changed
            boolean viewChanged = false;
            synchronized (this) {
                if (lastGraph == null) {
                    lastGraph = new WeakReference<>(graph);
                } else {
                    Graph lg = lastGraph.get();
                    lastGraph = null;
                    if (lg == null || lg != graph) {
                        viewChanged = true;
                        lastGraph = new WeakReference<>(graph);
                    }
                }

                // Check if transformNullValues was changed
                if (lastTransformNullValues != model.isTransformNullValues()) {
                    viewChanged = true;
                }
                lastTransformNullValues = model.isTransformNullValues();
            }

            int newVersion = partition.getVersion(graph);
            return version.getAndSet(newVersion) != newVersion || viewChanged;
        }
        return false;
    }

    @Override
    public boolean isValid() {<FILL_FUNCTION_BODY>}

    @Override
    public Graph getGraph() {
        if (isRanking()) {
            return model.getRankingGraph();
        } else if (isPartition()) {
            return model.getPartitionGraph();
        }
        return model.getGraphModel().getGraph();
    }

    @Override
    public Transformer getTransformer() {
        return transformer;
    }

    @Override
    public TransformerUI getUI() {
        return transformerUI;
    }

    @Override
    public boolean isSimple() {
        return ranking == null && partition == null;
    }

    @Override
    public boolean isAttribute() {
        return column != null;
    }

    @Override
    public boolean isPartition() {
        return partition != null;
    }

    @Override
    public boolean isRanking() {
        return ranking != null;
    }

    @Override
    public Class<? extends Element> getElementClass() {
        return elementClass;
    }

    @Override
    public AppearanceModelImpl getModel() {
        return model;
    }

    @Override
    public String toString() {
        return name;
    }

    @Override
    public String getId() {
        return name;
    }

    @Override
    public int hashCode() {
        int hash = 5;
        hash = 97 * hash + (this.name != null ? this.name.hashCode() : 0);
        return hash;
    }

    @Override
    public boolean equals(Object obj) {
        if (this == obj) {
            return true;
        }
        if (obj == null) {
            return false;
        }
        if (getClass() != obj.getClass()) {
            return false;
        }
        final FunctionImpl other = (FunctionImpl) obj;
        return Objects.equals(this.name, other.name);
    }
}

        Graph graph = getGraph();
        if (graph.getView().isDestroyed()) {
            return false;
        }
        if (isRanking()) {
            return ranking.isValid(graph);
        } else if (isPartition()) {
            return partition.isValid(graph);
        }
        return true;
    
<no_super_class>