<commit_before>/**
 * DelegatingPointSetGraphic.java
 * Created Jan 22, 2011
 */
package com.googlecode.blaisemath.graphics.core;

/*
 * #%L
 * BlaiseGraphics
 * --
 * Copyright (C) 2009 - 2016 <NAME>
 * --
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * 
 *      http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 * #L%
 */

import com.google.common.base.Functions;
import static com.google.common.base.Preconditions.checkNotNull;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.google.common.collect.Sets;
import com.googlecode.blaisemath.annotation.InvokedFromThread;
import static com.googlecode.blaisemath.graphics.core.LabeledPointGraphic.LABEL_RENDERER_PROP;
import static com.googlecode.blaisemath.graphics.core.PrimitiveGraphicSupport.RENDERER_PROP;
import com.googlecode.blaisemath.style.ObjectStyler;
import com.googlecode.blaisemath.style.Renderer;
import com.googlecode.blaisemath.style.Styles;
import com.googlecode.blaisemath.util.AnchoredText;
import com.googlecode.blaisemath.util.coordinate.CoordinateChangeEvent;
import com.googlecode.blaisemath.util.coordinate.CoordinateListener;
import com.googlecode.blaisemath.util.coordinate.CoordinateManager;
import com.googlecode.blaisemath.util.swing.BSwingUtilities;
import java.awt.geom.Point2D;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import javax.swing.JPopupMenu;

/**
 * Manages a collection of points that are maintained as separate {@link Graphic}s,
 * and therefore fully customizable. Point locations are handled by a {@link CoordinateManager},
 * which allows their locations to be safely modified from other threads.
 *
 * @param <S> the type of object being displayed
 * @param <G> type of canvas to render to
 *
 * @see BasicPointSetGraphic
 *
 * @author <NAME>
 */
public class DelegatingPointSetGraphic<S,G> extends GraphicComposite<G> {
    
    private static final int NODE_CACHE_SIZE = 20000;
    
    /** Key for flag allowing individual points to be selected */
    public static final String POINT_SELECTION_ENABLED = "point-selection-enabled";

    /** Graphic objects for individual points */
    protected final Map<S, DelegatingPrimitiveGraphic<S,Point2D,G>> points = Maps.newHashMap();
    /** Whether points can be dragged */
    protected boolean dragEnabled = false;

    /** Manages locations of points */
    protected CoordinateManager<S,Point2D> manager;
    /** Selects styles for graphics */
    @Nonnull 
    protected ObjectStyler<S> styler = ObjectStyler.create();
    /** Selects renderer for points */
    protected Renderer<Point2D,G> renderer;
    /** Renderer for point labels */
    protected Renderer<AnchoredText,G> textRenderer;

    /** Indicates points are being updated */
    protected boolean updatingPoint = false;
    /** Responds to coordinate update events */
    private final CoordinateListener coordListener;
    
    
    /**
     * Construct with no points
     */
    public DelegatingPointSetGraphic() {
        this(null, null);
    }
    
    /**
     * Construct with no points
     * @param renderer draws points
     * @param labelRenderer draws labels
     */
    public DelegatingPointSetGraphic(@Nullable Renderer<Point2D, G> renderer,
            @Nullable Renderer<AnchoredText, G> labelRenderer) {
        this(CoordinateManager.<S, Point2D>create(NODE_CACHE_SIZE), renderer, labelRenderer);
    }

    /**
     * Construct with source objects and locations as a map
     * @param crdManager manages point locations
     * @param renderer used for drawing the points
     * @param labelRenderer draws labels
     */
    public DelegatingPointSetGraphic(CoordinateManager<S, Point2D> crdManager, 
            @Nullable Renderer<Point2D, G> renderer,
            @Nullable Renderer<AnchoredText, G> labelRenderer) {
        setRenderer(renderer);
        setLabelRenderer(labelRenderer);
        
        styler.setStyleConstant(Styles.DEFAULT_POINT_STYLE);
        styler.setTipDelegate(Functions.toStringFunction());
        
        coordListener = new CoordinateListener(){
            @Override
            @InvokedFromThread("unknown")
            public void coordinatesChanged(final CoordinateChangeEvent evt) {
                BSwingUtilities.invokeOnEventDispatchThread(new Runnable(){
                    @Override
                    public void run() {
                        updatePointGraphics(evt.getAdded(), evt.getRemoved());
                    }
                });
            }
        };
        
        setCoordinateManager(crdManager);
    }
    

    //<editor-fold defaultstate="collapsed" desc="EVENT HANDLERS">
    //
    // EVENT HANDLERS
    //
    
    private void updatePointGraphics(Map<S,Point2D> added, Set<S> removed) {
        List<Graphic<G>> addMe = Lists.newArrayList();
        if (added != null) {
            for (Entry<S, Point2D> en : added.entrySet()) {
                S src = en.getKey();
                DelegatingPrimitiveGraphic<S,Point2D,G> dpg = points.get(src);
                if (dpg == null) {
                    LabeledPointGraphic<S,G> lpg = new LabeledPointGraphic<S,G>(en.getKey(), en.getValue(), styler);
                    lpg.setRenderer(renderer);
                    lpg.setLabelRenderer(textRenderer);
                    lpg.setDragEnabled(dragEnabled);
                    lpg.setSelectionEnabled(isPointSelectionEnabled());
                    points.put(src, lpg);
                    addMe.add(lpg);
                } else {
                    // this should not result in manager changing
                    updatingPoint = true;
                    dpg.setPrimitive(en.getValue());
                    updatingPoint = false;
                }
            }
        }
        Set<DelegatingPrimitiveGraphic<S,Point2D,G>> removeMe = Sets.newHashSet();
        if (removed != null) {
            for (S s : removed) {
                removeMe.add(points.get(s));
                points.remove(s);
            }
        }
        replaceGraphics(removeMe, addMe);
    }

    @Override
    public void graphicChanged(Graphic<G> source) {
        if (!updatingPoint && source instanceof LabeledPointGraphic) {
            LabeledPointGraphic<S,G> dpg = (LabeledPointGraphic<S,G>) source;
            manager.put(dpg.getSourceObject(), dpg.getPrimitive());
        }<commit_msg>BlaiseGraphics fix issue w/ updating points when only locations change (didn't propagate event properly)<commit_after>/**
 * DelegatingPointSetGraphic.java
 * Created Jan 22, 2011
 */
package com.googlecode.blaisemath.graphics.core;

/*
 * #%L
 * BlaiseGraphics
 * --
 * Copyright (C) 2009 - 2016 <NAME>
 * --
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * 
 *      http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 * #L%
 */

import com.google.common.base.Functions;
import static com.google.common.base.Preconditions.checkNotNull;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.google.common.collect.Sets;
import com.googlecode.blaisemath.annotation.InvokedFromThread;
import static com.googlecode.blaisemath.graphics.core.LabeledPointGraphic.LABEL_RENDERER_PROP;
import static com.googlecode.blaisemath.graphics.core.PrimitiveGraphicSupport.RENDERER_PROP;
import com.googlecode.blaisemath.style.ObjectStyler;
import com.googlecode.blaisemath.style.Renderer;
import com.googlecode.blaisemath.style.Styles;
import com.googlecode.blaisemath.util.AnchoredText;
import com.googlecode.blaisemath.util.coordinate.CoordinateChangeEvent;
import com.googlecode.blaisemath.util.coordinate.CoordinateListener;
import com.googlecode.blaisemath.util.coordinate.CoordinateManager;
import com.googlecode.blaisemath.util.swing.BSwingUtilities;
import java.awt.geom.Point2D;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import javax.swing.JPopupMenu;

/**
 * Manages a collection of points that are maintained as separate {@link Graphic}s,
 * and therefore fully customizable. Point locations are handled by a {@link CoordinateManager},
 * which allows their locations to be safely modified from other threads.
 *
 * @param <S> the type of object being displayed
 * @param <G> type of canvas to render to
 *
 * @see BasicPointSetGraphic
 *
 * @author <NAME>
 */
public class DelegatingPointSetGraphic<S,G> extends GraphicComposite<G> {
    
    private static final int NODE_CACHE_SIZE = 20000;
    
    /** Key for flag allowing individual points to be selected */
    public static final String POINT_SELECTION_ENABLED = "point-selection-enabled";

    /** Graphic objects for individual points */
    protected final Map<S, DelegatingPrimitiveGraphic<S,Point2D,G>> points = Maps.newHashMap();
    /** Whether points can be dragged */
    protected boolean dragEnabled = false;

    /** Manages locations of points */
    protected CoordinateManager<S,Point2D> manager;
    /** Selects styles for graphics */
    @Nonnull 
    protected ObjectStyler<S> styler = ObjectStyler.create();
    /** Selects renderer for points */
    protected Renderer<Point2D,G> renderer;
    /** Renderer for point labels */
    protected Renderer<AnchoredText,G> textRenderer;

    /** Indicates points are being updated */
    protected boolean updatingPoint = false;
    /** Responds to coordinate update events */
    private final CoordinateListener coordListener;
    
    
    /**
     * Construct with no points
     */
    public DelegatingPointSetGraphic() {
        this(null, null);
    }
    
    /**
     * Construct with no points
     * @param renderer draws points
     * @param labelRenderer draws labels
     */
    public DelegatingPointSetGraphic(@Nullable Renderer<Point2D, G> renderer,
            @Nullable Renderer<AnchoredText, G> labelRenderer) {
        this(CoordinateManager.<S, Point2D>create(NODE_CACHE_SIZE), renderer, labelRenderer);
    }

    /**
     * Construct with source objects and locations as a map
     * @param crdManager manages point locations
     * @param renderer used for drawing the points
     * @param labelRenderer draws labels
     */
    public DelegatingPointSetGraphic(CoordinateManager<S, Point2D> crdManager, 
            @Nullable Renderer<Point2D, G> renderer,
            @Nullable Renderer<AnchoredText, G> labelRenderer) {
        setRenderer(renderer);
        setLabelRenderer(labelRenderer);
        
        styler.setStyleConstant(Styles.DEFAULT_POINT_STYLE);
        styler.setTipDelegate(Functions.toStringFunction());
        
        coordListener = new CoordinateListener(){
            @Override
            @InvokedFromThread("unknown")
            public void coordinatesChanged(final CoordinateChangeEvent evt) {
                BSwingUtilities.invokeOnEventDispatchThread(new Runnable(){
                    @Override
                    public void run() {
                        updatePointGraphics(evt.getAdded(), evt.getRemoved());
                    }
                });
            }
        };
        
        setCoordinateManager(crdManager);
    }
    

    //<editor-fold defaultstate="collapsed" desc="EVENT HANDLERS">
    //
    // EVENT HANDLERS
    //
    
    private void updatePointGraphics(Map<S,Point2D> added, Set<S> removed) {
        boolean change = false;
        List<Graphic<G>> addMe = Lists.newArrayList();
        if (added != null) {
            for (Entry<S, Point2D> en : added.entrySet()) {
                S src = en.getKey();
                DelegatingPrimitiveGraphic<S,Point2D,G> dpg = points.get(src);
                if (dpg == null) {
                    LabeledPointGraphic<S,G> lpg = new LabeledPointGraphic<S,G>(en.getKey(), en.getValue(), styler);
                    lpg.setRenderer(renderer);
                    lpg.setLabelRenderer(textRenderer);
                    lpg.setDragEnabled(dragEnabled);
                    lpg.setSelectionEnabled(isPointSelectionEnabled());
                    points.put(src, lpg);
                    addMe.add(lpg);
                } else {
                    // this should not result in manager changing
                    updatingPoint = true;
                    dpg.setPrimitive(en.getValue());
                    updatingPoint = false;
                    change = true;
                }
            }
        }
        Set<DelegatingPrimitiveGraphic<S,Point2D,G>> removeMe = Sets.newHashSet();
        if (removed != null) {
            for (S s : removed) {
                removeMe.add(points.get(s));
                points.remove(s);
            }
        }
        change = replaceGraphics(removeMe, addMe) || change;
        if (change) {
            super.graphicChanged(this);
        }
    }

    @Override
    public void graphicChanged(Graphic<G> source) {
        if (!updatingPoint && source instanceof LabeledPointGraphic) {
            LabeledPointGraphic<S,G> dpg = (LabeledPointGraphic<S,G>) source;
            manager.put(dpg.getSourceObject(), dpg.getPrimitive());
        }
{'fraction_non_alphanumeric': 0.0955585464333782, 'fraction_numerical': 0.004958560600694198, 'mean_word_length': 2.719178082191781, 'pattern_counts': {'":': 0, '<': 67, '<?xml version=': 0, '>': 67, 'https://': 0, 'lorem ipsum': 0, 'www.': 2, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '4235646', 'n_tokens_mistral': 3965, 'n_tokens_neox': 3872, 'n_words': 977}