Package org.locationtech.udig.project.internal.render.impl

Source Code of org.locationtech.udig.project.internal.render.impl.RenderExecutorImpl$RendererListener

/**
* <copyright></copyright> $Id$
*/
package org.locationtech.udig.project.internal.render.impl;

import java.awt.Graphics2D;
import java.awt.Point;
import java.awt.Rectangle;
import java.text.MessageFormat;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.concurrent.atomic.AtomicBoolean;

import org.locationtech.udig.project.ILayer;
import org.locationtech.udig.project.internal.Layer;
import org.locationtech.udig.project.internal.Messages;
import org.locationtech.udig.project.internal.ProjectPackage;
import org.locationtech.udig.project.internal.ProjectPlugin;
import org.locationtech.udig.project.internal.impl.SynchronizedEList;
import org.locationtech.udig.project.internal.render.ExecutorVisitor;
import org.locationtech.udig.project.internal.render.RenderContext;
import org.locationtech.udig.project.internal.render.RenderExecutor;
import org.locationtech.udig.project.internal.render.RenderManager;
import org.locationtech.udig.project.internal.render.RenderPackage;
import org.locationtech.udig.project.internal.render.Renderer;
import org.locationtech.udig.project.internal.render.SelectionLayer;
import org.locationtech.udig.project.render.ILabelPainter;
import org.locationtech.udig.project.render.IRenderContext;
import org.locationtech.udig.project.render.IRenderer;
import org.locationtech.udig.project.render.RenderException;

import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.NullProgressMonitor;
import org.eclipse.core.runtime.jobs.IJobChangeEvent;
import org.eclipse.core.runtime.jobs.IJobChangeListener;
import org.eclipse.core.runtime.jobs.JobChangeAdapter;
import org.eclipse.emf.common.notify.Adapter;
import org.eclipse.emf.common.notify.Notification;
import org.eclipse.emf.common.notify.impl.AdapterImpl;
import org.eclipse.emf.ecore.EClass;
import org.eclipse.emf.ecore.EStructuralFeature;
import org.eclipse.emf.ecore.impl.ENotificationImpl;

import com.vividsolutions.jts.geom.Coordinate;
import com.vividsolutions.jts.geom.Envelope;

/**
* Runs a renderer in its own thread. Is responsible for stopping the thread. Does not do composite
* renderer or multilayer renderers.
*
* @author Jesse
* @since 1.0.0
*/
public class RenderExecutorImpl extends RendererImpl implements RenderExecutor {
    /**
     * Listens to a layer for visibility events and styling events. <b>Public ONLY for testing
     * purposes</b>
     *
     * @author Jesse
     * @since 1.0.0
     */
    public static class LayerListener extends AdapterImpl {
        protected RenderExecutorImpl executor;

        LayerListener(RenderExecutorImpl executor) {
            this.executor = executor;
        }

        /**
         * @see org.eclipse.emf.common.notify.impl.AdapterImpl#notifyChanged(org.eclipse.emf.common.notify.Notification)
         */
        public void notifyChanged(Notification msg) {
            Layer layer = (Layer) msg.getNotifier();
            switch (msg.getFeatureID(Layer.class)) {
            case ProjectPackage.LAYER__STYLE_BLACKBOARD:
                //dealt with by the layer listener created in the RenderManagerAdapters (added to the render manager)
                //therefore we don't need to deal with this here.
                //                if (executor.getContext().getLayer() instanceof SelectionLayer)
                //                    return;
                //
                //                styleBlackboardChanged(msg);
                break;
            case ProjectPackage.LAYER__VISIBLE:
                if (executor.getContext().getLayer() instanceof SelectionLayer)
                    return;
                if (msg.getNewBooleanValue())
                    layerVisible(layer, msg);
                else
                    layerNotVisible(layer, msg);
                break;

            default:
                break;
            }
        }

        protected void layerNotVisible(Layer layer, Notification msg) {
            RenderContext context2 = executor.getContext();
            context2.getLabelPainter().disableLayer(context2.getLayer().getID().toString());
            if (executor.getState() == RENDERING) {
                executor.stopRendering();
                context2.setStatus(ILayer.DONE);
                context2.setStatusMessage(""); //$NON-NLS-1$
                executor.setState(NEVER);
            } else {
                RenderManager renderManager = (RenderManager) layer.getMapInternal()
                        .getRenderManager();
                if (renderManager != null) {
                    renderManager.refreshImage();
                }
            }
        }

        protected void layerVisible(Layer layer, Notification msg) {
            RenderContext context2 = executor.getContext();
            context2.getLabelPainter().enableLayer(context2.getLayer().getID().toString());
            if (executor.getState() == IRenderer.RENDERING)
                return;
            if (executor.getState() != IRenderer.DONE || executor.dirty) {
                RenderManager renderManager = (RenderManager) layer.getMapInternal()
                        .getRenderManager();
                renderManager.refresh(layer, null); //ensures the entire layer and all tiles/selection layers are refreshed
                //executor.getRenderer().setState(RENDER_REQUEST);
            } else {
                RenderManager renderManager = (RenderManager) layer.getMapInternal()
                        .getRenderManager();
                if (renderManager != null) {
                    renderManager.refreshImage();
                }
            }
        }

        //        protected void styleBlackboardChanged( Notification msg ) {
        //            executor.getContext().getRenderManager().refresh((ILayer) msg.getNotifier(), null);
        //        }
    }

    /**
     * Calls the render() function when a RENDER_REQUEST goes by...
     * <p>
     * This listener will call the following for a RENDER_REQUEST in the following order:
     * <ul>
     * <li>executor.setRenderBounds
     * <li>executor.render()
     * <li>executor.setState
     * </ul>
     * Right now we assume it is listening to EVERTHING (or at least everything
     * in this Map).
     */
    protected static class RendererListener extends AdapterImpl {

        protected RenderExecutor executor;

        RendererListener(RenderExecutor executor) {
            this.executor = executor;
        }

        /**
         * @see org.eclipse.emf.common.notify.impl.AdapterImpl#notifyChanged(org.eclipse.emf.common.notify.Notification)
         */
        public void notifyChanged(Notification msg) {
            if (msg.getNotifier() instanceof Renderer) {
                if (msg.getFeatureID(Renderer.class) == RenderPackage.RENDERER__STATE) {
                    if (msg.getNewIntValue() == IRenderer.RENDER_REQUEST) {
                        executor.setRenderBounds(executor.getRenderer().getRenderBounds());
                    }
                    stateChanged(msg);
                }
            }

        }

        protected void stateChanged(Notification msg) {
            executor.setState(msg.getNewIntValue());
            if (msg.getNewIntValue() == IRenderer.RENDER_REQUEST) {
                try {
                    executor.render();
                } catch (RenderException e) {
                    // won't happen
                    throw (RuntimeException) new RuntimeException().initCause(e);
                }
            }

        }
    }

    protected static void clearImage(Envelope bounds2, RenderExecutor executor) {
        if (bounds2 != null
                && !bounds2.contains(executor.getContext().getViewportModel().getBounds())) {
            Point min = executor.getContext().worldToPixel(
                    new Coordinate(bounds2.getMinX(), bounds2.getMinY()));
            Point max = executor.getContext().worldToPixel(
                    new Coordinate(bounds2.getMaxX(), bounds2.getMaxY()));
            int width = Math.abs(max.x - min.x);
            int height = Math.abs(max.y - min.y);
            Rectangle paintArea = new Rectangle(Math.min(min.x, max.x), Math.min(min.y, max.y),
                    width, height);
            executor.getContext().clearImage(paintArea);
        } else {
            executor.getContext().clearImage();
        }
    }

    /**
     * The cached value of the '{@link #getRenderer() <em>Renderer</em>}' reference. <!--
     * begin-user-doc --> <!-- end-user-doc -->
     *
     * @see #getRenderer()
     * @generated
     * @ordered
     */
    protected Renderer renderer;

    protected Adapter renderListener = getRendererListener();

    /**
     * A listener that triggers an update when the style of its layer changes or the visibility
     * changes.
     */
    protected Adapter layerListener = getLayerListener();

    protected RenderJob renderJob;

    protected volatile boolean dirty;

    /**
     * <!-- begin-user-doc --> <!-- end-user-doc -->
     *
     * @generated NOT
     */
    public RenderExecutorImpl() {
        super();
        renderJob = new RenderJob(this);
    }

    /**
     * <!-- begin-user-doc --> <!-- end-user-doc -->
     * @generated
     */
    @Override
    protected EClass eStaticClass() {
        return RenderPackage.Literals.RENDER_EXECUTOR;
    }

    /**
     * <!-- begin-user-doc --> <!-- end-user-doc -->
     *
     * @generated NOT
     */
    public void dispose() {
        while (getRenderer().eAdapters().remove(renderListener))
            ;
        eAdapters().clear();
        removeLayerListener(getContext());
        stopRendering();
        getRenderer().dispose();

        try {
            //Clear label cache for this layer.
            ILayer renderingLayer = getContext().getLayer();
            if (renderingLayer != null) {
                //Only if it as a usual layer's renderer
                ILabelPainter labelPainter = getContext().getLabelPainter();
                String layerId = renderingLayer.getID().toString();
                if (getContext().getLayer() instanceof SelectionLayer)
                    layerId = layerId + "-Selection"; //$NON-NLS-1$
                labelPainter.clear(layerId);
            }
        } catch (Exception exc) {
            exc.printStackTrace();
        }

        if (getRenderer().getState() != DISPOSED)
            getRenderer().setState(DISPOSED);
    }

    /**
     * @see org.locationtech.udig.project.internal.render.impl.RendererImpl#setState(int)
     */
    public void setState(int newState) {
        super.setState(newState);
    }

    /**
     * <!-- begin-user-doc --> <!-- end-user-doc -->
     *
     * @generated NOT
     */
    public void stopRendering() {
        if (renderJob.cancel())
            return;
        final AtomicBoolean done = new AtomicBoolean(renderJob.cancel());
        IJobChangeListener listener = new JobChangeAdapter() {
            @Override
            public void done(IJobChangeEvent event) {
                done.set(true);
                synchronized (done) {
                    done.notify();
                }

            }
        };
        renderJob.addJobChangeListener(listener);
        try {
            long start = System.currentTimeMillis();
            while (!done.get() && !renderJob.cancel() && start + 2000 < System.currentTimeMillis())
                synchronized (done) {
                    try {
                        done.wait(200);
                    } catch (InterruptedException e) {
                        throw (RuntimeException) new RuntimeException().initCause(e);
                    }
                }
            if (!done.get() && !renderJob.cancel()) {
                ProjectPlugin
                        .log("After 2 seconds unable to cancel " + getRenderer().getName() + " Renderer"); //$NON-NLS-1$ //$NON-NLS-2$
            }
        } finally {
            renderJob.removeJobChangeListener(listener);
        }

    }

    /**
     * <!-- begin-user-doc --> <!-- end-user-doc -->
     *
     * @throws RenderException
     * @generated NOT
     */
    public void render(Graphics2D destination, IProgressMonitor monitor) throws RenderException {

        if (getState() == DISPOSED)
            return;
        getRenderer().render(destination, validateMonitor(monitor));
    }

    private IProgressMonitor validateMonitor(IProgressMonitor monitor) {
        if (monitor != null)
            return monitor;

        return new NullProgressMonitor();
    }

    /**
     * @see org.locationtech.udig.project.internal.render.Renderer#getContext()
     */
    public RenderContext getContext() {
        if (getRenderer() == null)
            return null;
        return (RenderContext) getRenderer().getContext();
    }

    /**
     * <!-- begin-user-doc --> <!-- end-user-doc -->
     * @generated
     */
    public Renderer getRenderer() {
        return renderer;
    }

    protected String getRenderJobName() {
        return MessageFormat
                .format(Messages.RenderExecutorImpl_message, new Object[] { getName() });
    }

    /**
     * @see org.locationtech.udig.project.internal.render.RenderExecutor#setRenderer(org.locationtech.udig.project.render.Renderer)
     * @uml.property name="renderer"
     */
    @SuppressWarnings("unchecked")
    public void setRenderer(Renderer newRenderer) {
        if (getRenderer() != null) {
            getRenderer().eAdapters().remove(renderListener);
            removeLayerListener((RenderContext) getRenderer().getContext());
        }
        setRendererGen(newRenderer);
        // registerFeatureListener();
        if (newRenderer != null) {
            newRenderer.eAdapters().add(renderListener);
            addLayerListener((RenderContext) newRenderer.getContext());
        }
    }

    @SuppressWarnings("unchecked")
    protected void removeLayerListener(IRenderContext context) {
        if (context.getLayer() != null) {
            Layer layer = ((Layer) context.getLayer());
            List<Adapter> adapters = layer.eAdapters();
            if (adapters instanceof SynchronizedEList) {
                ((SynchronizedEList) adapters).lock();
            }
            try {
                ArrayList<Adapter> toRemove = new ArrayList<Adapter>();
                for (Iterator<Adapter> iter = adapters.iterator(); iter.hasNext();) {
                    Adapter t = iter.next();
                    if (t instanceof RenderExecutorImpl.LayerListener) {
                        //                        iter.remove();
                        //iter.remove() doesn't seem to work here; nothing gets removed.
                        toRemove.add(t);
                    }
                }
                adapters.removeAll(toRemove);
            } finally {
                if (adapters instanceof SynchronizedEList) {
                    ((SynchronizedEList) adapters).unlock();
                }
            }
        }
    }

    @SuppressWarnings("unchecked")
    protected void addLayerListener(IRenderContext context) {

        if (context.getLayer() != null && !(context.getLayer() instanceof SelectionLayer)) {
            removeLayerListener(context);
            ((Layer) context.getLayer()).eAdapters().add(layerListener);
        }
    }

    /**
     * <!-- begin-user-doc --> <!-- end-user-doc -->
     * @generated
     */
    public void setRendererGen(Renderer newRenderer) {
        Renderer oldRenderer = renderer;
        renderer = newRenderer;
        if (eNotificationRequired())
            eNotify(new ENotificationImpl(this, Notification.SET,
                    RenderPackage.RENDER_EXECUTOR__RENDERER, oldRenderer, renderer));
    }

    /**
     * <!-- begin-user-doc -->
     * <!-- end-user-doc -->
     * @generated
     */
    @Override
    public Object eGet(int featureID, boolean resolve, boolean coreType) {
        switch (featureID) {
        case RenderPackage.RENDER_EXECUTOR__RENDERER:
            return getRenderer();
        }
        return super.eGet(featureID, resolve, coreType);
    }

    /**
     * <!-- begin-user-doc -->
     * <!-- end-user-doc -->
     * @generated
     */
    @Override
    public void eSet(int featureID, Object newValue) {
        switch (featureID) {
        case RenderPackage.RENDER_EXECUTOR__RENDERER:
            setRenderer((Renderer) newValue);
            return;
        }
        super.eSet(featureID, newValue);
    }

    /**
     * <!-- begin-user-doc -->
     * <!-- end-user-doc -->
     * @generated
     */
    @Override
    public void eUnset(int featureID) {
        switch (featureID) {
        case RenderPackage.RENDER_EXECUTOR__RENDERER:
            setRenderer((Renderer) null);
            return;
        }
        super.eUnset(featureID);
    }

    /**
     * <!-- begin-user-doc -->
     * <!-- end-user-doc -->
     * @generated
     */
    @Override
    public boolean eIsSet(int featureID) {
        switch (featureID) {
        case RenderPackage.RENDER_EXECUTOR__RENDERER:
            return renderer != null;
        }
        return super.eIsSet(featureID);
    }

    /**
     * @see org.locationtech.udig.project.internal.render.impl.RendererImpl#getInfo(Point, Layer)
     *      public InfoList getInfo(Point screenLocation) throws IOException { if
     *      (!(getContext().getLayer() instanceof SelectionLayer)) return
     *      renderer.getInfo(screenLocation); return new InfoList(screenLocation.x,
     *      screenLocation.y, null); }
     */

    /**
     * @see org.locationtech.udig.project.internal.render.RenderExecutor#visit(org.locationtech.udig.project.render.ExecutorVisitor)
     */
    public void visit(ExecutorVisitor visitor) {
        visitor.visit(this);
    }

    /**
     * @return Returns the layerListener.
     * @uml.property name="layerListener"
     */
    protected LayerListener getLayerListener() {
        return new LayerListener(this);
    }

    protected RendererListener getRendererListener() {
        return new RendererListener(this);
    }

    /**
     * @see org.locationtech.udig.project.internal.render.Renderer#render(com.vividsolutions.jts.geom.Envelope)
     */
    public synchronized void render() {
        if (getState() == DISPOSED || !getRenderer().getContext().isVisible()) {
            dirty = true;
            getContext().getLayer().setStatus(ILayer.DONE);
            if (getRenderer().getState() != IRenderer.DONE)
                getRenderer().setState(IRenderer.DONE);
            return;
        }

        dirty = false;
        if (getRenderer().getState() != STARTING) {
            getRenderer().setState(IRenderer.STARTING);
        }
        renderJob.addRequest(getRenderBounds());
    }

    /**
     * @see org.locationtech.udig.project.internal.render.impl.RendererImpl#render(com.vividsolutions.jts.geom.Envelope,
     *      org.eclipse.core.runtime.IProgressMonitor)
     */
    public void render(IProgressMonitor monitor) {
        render();
    }

    @Override
    public String toString() {
        String selection = ""; //$NON-NLS-1$
        if (getContext().getLayer() instanceof SelectionLayer)
            selection = "Selection "; //$NON-NLS-1$
        return getContext().getMap().getName()
                + ":" + selection + (getRenderer() != null ? getRenderer().getName() : "null"); //$NON-NLS-1$ //$NON-NLS-2$
    }

} // RenderExecutorImpl
TOP

Related Classes of org.locationtech.udig.project.internal.render.impl.RenderExecutorImpl$RendererListener

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.