001: /*
002: * uDig - User Friendly Desktop Internet GIS client http://udig.refractions.net (C) 2004,
003: * Refractions Research Inc. This library is free software; you can redistribute it and/or modify it
004: * under the terms of the GNU Lesser General Public License as published by the Free Software
005: * Foundation; version 2.1 of the License. This library is distributed in the hope that it will be
006: * useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
007: * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
008: */
009: package net.refractions.udig.project.internal.render.impl;
010:
011: import java.awt.Graphics2D;
012: import java.text.MessageFormat;
013: import java.util.Collection;
014:
015: import net.refractions.udig.project.ILayer;
016: import net.refractions.udig.project.internal.Messages;
017: import net.refractions.udig.project.internal.ProjectPlugin;
018: import net.refractions.udig.project.internal.render.ExecutorVisitor;
019: import net.refractions.udig.project.internal.render.RenderExecutor;
020: import net.refractions.udig.project.internal.render.Renderer;
021: import net.refractions.udig.project.render.IRenderContext;
022: import net.refractions.udig.project.render.IRenderer;
023: import net.refractions.udig.project.render.RenderException;
024:
025: import org.eclipse.core.runtime.IProgressMonitor;
026: import org.eclipse.core.runtime.IStatus;
027: import org.eclipse.core.runtime.Status;
028: import org.eclipse.core.runtime.jobs.Job;
029: import org.eclipse.emf.common.notify.Notification;
030:
031: import com.vividsolutions.jts.geom.Envelope;
032:
033: /**
034: * An Executor specifically for executing CompositeRenderers
035: *
036: * @author jeichar
037: * @since 0.6.0
038: */
039: public class RenderExecutorComposite extends RenderExecutorMultiLayer {
040:
041: int timeout = 0;
042:
043: /**
044: * Listens to the render events
045: *
046: * @author jeichar
047: * @since 0.6.0
048: */
049: protected static class CompositeRendererListener extends
050: MultiLayerRendererListener {
051:
052: /**
053: * Construct <code>CompositeRendererListener</code>.
054: *
055: * @param executor
056: */
057: CompositeRendererListener(RenderExecutorComposite executor) {
058: super (executor);
059: }
060:
061: RenderExecutorComposite getExecutor() {
062: return (RenderExecutorComposite) executor;
063: }
064:
065: /**
066: * @see net.refractions.udig.project.internal.render.impl.RenderExecutorMultiLayer.MultiLayerRendererListener#stateChanged(org.eclipse.emf.common.notify.Notification)
067: */
068: protected void stateChanged(Notification msg) {
069: switch (msg.getNewIntValue()) {
070: case Renderer.RENDERING:
071: getExecutor().resetTimeout();
072: synchronized (getExecutor()) {
073: // I think I want to do an update.
074: // lets make it so if a renderer says update then we do it.
075: getExecutor().setState(RENDERING);
076: }
077: break;
078: case Renderer.DONE:
079: try {
080: synchronized (getExecutor()) {
081: if (!getExecutor().inUpdate) {
082: getExecutor().getRenderer().refreshImage();
083: executor.setState(Renderer.DONE);
084: }
085: if (!isRendering(getExecutor().getRenderer())
086: || getExecutor().renderJob.getMonitor()
087: .isCanceled()) {
088: getExecutor().notifyAll();
089: }
090: }
091: // state will be set in next else statement
092: } catch (RenderException e) {
093: // won't happen.
094: ProjectPlugin.log("", e); //$NON-NLS-1$
095: }
096: break;
097: case Renderer.RENDER_REQUEST:
098: boolean oldValue = executor.eDeliver();
099: executor.eSetDeliver(false);
100: executor.setState(msg.getNewIntValue());
101: executor.eSetDeliver(oldValue);
102: ((RenderExecutorComposite) executor).refresh();
103: break;
104: case Renderer.STARTING:
105: boolean oldValue2 = executor.eDeliver();
106: executor.eSetDeliver(false);
107: executor.setState(msg.getNewIntValue());
108: executor.eSetDeliver(oldValue2);
109: break;
110: }
111: }
112:
113: }
114:
115: private boolean inUpdate = false;
116:
117: protected static class CompositeRendererJob extends RenderJob {
118:
119: private static final int TIMEOUT = 20000;
120:
121: // private static final int TIMEOUT = 2000000;
122:
123: /**
124: * Construct <code>CompositeRendererJob</code>.
125: *
126: * @param executor
127: */
128: public CompositeRendererJob(RenderExecutorComposite executor) {
129: super (executor);
130: setPriority(Job.INTERACTIVE);
131: }
132:
133: @Override
134: protected void init() {
135: }
136:
137: /**
138: * @see net.refractions.udig.project.internal.render.impl.RenderExecutorImpl.RenderJob#getExecutor()
139: */
140: public RenderExecutorComposite getExecutor() {
141: return (RenderExecutorComposite) executor;
142: }
143:
144: /**
145: * @see net.refractions.udig.project.internal.render.impl.RenderExecutorImpl.RenderJob#startRendering()
146: */
147: protected void startRendering(Envelope bounds,
148: IProgressMonitor monitor) throws Throwable {
149: // need to show that we are in update just in case a renderer triggers a state event
150: // in the RenderExecutor.render() method.
151: synchronized (getExecutor()) {
152: getExecutor().inUpdate = true;
153: }
154: super .startRendering(bounds, monitor);
155: incrementalUpdate();
156: }
157:
158: @Override
159: protected void initializeLabelPainter(IRenderContext context2) {
160: // do nothing
161: }
162:
163: @Override
164: protected void finalizeLabelPainter(IRenderContext context2) {
165: // do nothing
166: }
167:
168: /**
169: * Incrementally updates the display until rendering is complete.
170: *
171: * @throws InterruptedException
172: * @throws RenderException
173: */
174: public void incrementalUpdate() throws InterruptedException,
175: RenderException {
176: getExecutor().resetTimeout();
177: final int wait_period = 700;
178:
179: synchronized (getExecutor()) {
180: getExecutor().inUpdate = true;
181: }
182:
183: CompositeRendererImpl renderer2 = getExecutor()
184: .getRenderer();
185: synchronized (getExecutor()) {
186: while ((getMonitor() != null && !getMonitor()
187: .isCanceled())
188: && isRendering(renderer2)
189: && getExecutor().timeout < TIMEOUT) {
190: long start = System.currentTimeMillis();
191: getExecutor().wait(wait_period);
192: getExecutor().timeout += start
193: - System.currentTimeMillis();
194: if (isRendering(renderer2)) {
195: renderer2.refreshImage();
196: getExecutor().setState(RENDERING);
197: }
198: }
199: }
200: synchronized (getExecutor()) {
201: renderer2.refreshImage();
202: getExecutor().setState(IRenderer.DONE);
203: Graphics2D g = getExecutor().getContext().getImage()
204: .createGraphics();
205: g.dispose();
206: getExecutor().inUpdate = false;
207: }
208: }
209:
210: /**
211: * @see net.refractions.udig.project.internal.render.impl.RenderExecutorImpl.RenderJob#postRendering()
212: */
213: protected void postRendering() {
214:
215: if (monitor.isCanceled()) {
216: executor.getRenderer().setState(CANCELLED);
217: for (RenderExecutor renderer : ((CompositeRendererImpl) executor
218: .getRenderer()).getRenderExecutors()) {
219: if (renderer.getContext().isVisible()
220: && executor.getState() != IRenderer.DONE) {
221: renderer.getContext().setStatus(ILayer.WARNING);
222: renderer
223: .getContext()
224: .setStatusMessage(
225: "Timed out while rendering this layer. Seems to have blocked, check that the server is up"); //$NON-NLS-1$
226: }
227: }
228: return;
229: }
230: }
231: }
232:
233: /**
234: *
235: * @param renderer2
236: * @return
237: */
238: static boolean isRendering(CompositeRendererImpl renderer2) {
239: Collection<RenderExecutor> executors = renderer2
240: .getRenderExecutors();
241: for (RenderExecutor executor : executors) {
242: if (executor.getContext().isVisible()
243: && (executor.getState() == IRenderer.RENDERING
244: || executor.getState() == IRenderer.STARTING || executor
245: .getState() == IRenderer.RENDER_REQUEST))
246: return true;
247: }
248: return false;
249: }
250:
251: /**
252: * Construct <code>CompositeRendererExecutorImpl</code>.
253: */
254: public RenderExecutorComposite() {
255: renderJob = new CompositeRendererJob(this );
256: }
257:
258: /**
259: * @see net.refractions.udig.project.internal.render.impl.RenderExecutorMultiLayer#registerFeatureListener()
260: */
261: protected void registerFeatureListener() {
262: // do nothing
263: }
264:
265: /**
266: * Signals that the timeout for rendering should be reset.
267: * <p>
268: * The timeout is used to determine whether a renderer has stalled
269: * </p>
270: */
271: protected synchronized void resetTimeout() {
272: timeout = 0;
273: }
274:
275: /**
276: * @see net.refractions.udig.project.internal.render.impl.RenderExecutorImpl#getRenderer()
277: */
278: public CompositeRendererImpl getRenderer() {
279: return (CompositeRendererImpl) renderer;
280: }
281:
282: /**
283: * @see net.refractions.udig.project.internal.render.impl.RenderExecutorImpl#setRenderer(net.refractions.udig.project.render.Renderer)
284: */
285: public void setRenderer(Renderer newRenderer) {
286: renderJob.setSystem(false);
287: super .setRendererInternal(newRenderer);
288: }
289:
290: /**
291: * @see net.refractions.udig.project.internal.render.impl.RenderExecutorImpl#stopRendering()
292: */
293: public void stopRendering() {
294: for (RenderExecutor executor : getRenderer()
295: .getRenderExecutors()) {
296: executor.stopRendering();
297: }
298: super .stopRendering();
299: }
300:
301: protected void resyncState(Renderer renderer) {
302:
303: // FIXME sync sub renderers, witness composite renderer
304: //
305: // for( Renderer child : parent.children() ) resyncState( child );
306: }
307:
308: @Override
309: protected RendererListener getRendererListener() {
310: return new CompositeRendererListener(this );
311: }
312:
313: @Override
314: protected LayerListener getLayerListener() {
315: return new LayerListener(this ) {
316: @Override
317: public void notifyChanged(Notification msg) {
318: // do nothingsuper.notifyChanged(msg);
319: }
320: };
321: }
322:
323: @Override
324: protected String getRenderJobName() {
325: return MessageFormat.format(
326: Messages.RenderExecutorImpl_message,
327: new Object[] { getContext().getMap().getName() });
328: }
329:
330: /**
331: * @see net.refractions.udig.project.internal.render.impl.RenderExecutorImpl#visit(net.refractions.udig.project.render.ExecutorVisitor)
332: */
333: public void visit(ExecutorVisitor visitor) {
334: visitor.visit(this );
335: }
336:
337: Job refreshJob;
338:
339: /**
340: * Continually refresh the display until all renderers are done.
341: */
342: public synchronized void refresh() {
343: if (refreshJob == null) {
344: refreshJob = new Job(getRenderJobName()) {
345: @Override
346: protected IStatus run(IProgressMonitor monitor) {
347: try {
348: ((CompositeRendererJob) renderJob)
349: .incrementalUpdate();
350: } catch (Exception e) {
351: // log error
352: ProjectPlugin.log(null, e);
353: }
354: return Status.OK_STATUS;
355: }
356:
357: };
358: refreshJob.setSystem(false);
359: refreshJob.setPriority(Job.INTERACTIVE);
360: }
361: refreshJob.schedule();
362: }
363:
364: @Override
365: public synchronized void render() {
366:
367: RedrawJob runnable = new RedrawJob();
368: runnable.bounds = getRenderBounds();
369: if (redraw != null)
370: redraw.interrupt();
371: redraw = new Thread(runnable);
372: redraw.start();
373: }
374:
375: private volatile Thread redraw;
376:
377: class RedrawJob implements Runnable {
378:
379: Envelope bounds;
380:
381: public void run() {
382:
383: stopRendering();
384:
385: if (!Thread.currentThread().isInterrupted())
386: runRenderJob();
387: return;
388: }
389:
390: void runRenderJob() {
391: dirty = false;
392: if (getContext().getMapDisplay() == null
393: || getContext().getMapDisplay().getWidth() < 1
394: || getContext().getMapDisplay().getHeight() < 1)
395: return;
396: if (RenderExecutorComposite.this .getState() == IRenderer.DISPOSED)
397: throw new RuntimeException(
398: "attempted to run a disposed renderer"); //$NON-NLS-1$
399:
400: clearImage(bounds, RenderExecutorComposite.this);
401: renderJob.setBounds(bounds);
402:
403: renderJob.setName(getRenderJobName());
404: if (!Thread.currentThread().isInterrupted()) {
405: renderJob.schedule();
406: redraw = null;
407: }
408: }
409:
410: };
411: }
|