001: /**
002: * <copyright></copyright> $Id: RendererCreatorImpl.java 23326 2006-12-08 02:42:32Z jeichar $
003: */package net.refractions.udig.project.internal.render.impl;
004:
005: import java.awt.Graphics2D;
006: import java.io.IOException;
007: import java.util.ArrayList;
008: import java.util.Collection;
009: import java.util.Collections;
010: import java.util.HashMap;
011: import java.util.HashSet;
012: import java.util.Iterator;
013: import java.util.List;
014: import java.util.Map;
015: import java.util.Set;
016: import java.util.SortedSet;
017: import java.util.TreeSet;
018:
019: import net.refractions.udig.core.internal.ExtensionPointUtil;
020: import net.refractions.udig.project.ILayer;
021: import net.refractions.udig.project.internal.ContextModel;
022: import net.refractions.udig.project.internal.Layer;
023: import net.refractions.udig.project.internal.ProjectPackage;
024: import net.refractions.udig.project.internal.ProjectPlugin;
025: import net.refractions.udig.project.internal.render.CompositeRenderContext;
026: import net.refractions.udig.project.internal.render.RenderContext;
027: import net.refractions.udig.project.internal.render.RenderManager;
028: import net.refractions.udig.project.internal.render.Renderer;
029: import net.refractions.udig.project.internal.render.RendererCreator;
030: import net.refractions.udig.project.internal.render.SelectionLayer;
031: import net.refractions.udig.project.internal.render.impl.InternalRenderMetricsFactory.InternalRenderMetrics;
032: import net.refractions.udig.project.render.IRenderContext;
033: import net.refractions.udig.project.render.IRenderMetrics;
034: import net.refractions.udig.project.render.IRenderMetricsFactory;
035: import net.refractions.udig.project.render.IRenderer;
036:
037: import org.eclipse.core.runtime.IProgressMonitor;
038: import org.eclipse.emf.common.notify.Notification;
039: import org.geotools.data.FeatureSource;
040:
041: /**
042: * Default implementation
043: *
044: * @author Jesse
045: * @since 1.0.0
046: * @generated
047: */
048: public class RendererCreatorImpl implements RendererCreator {
049:
050: /**
051: * The cached value of the '{@link #getContext() <em>Context</em>}' reference.
052: *
053: * @see #getContext()
054: * @generated NOT
055: */
056: protected volatile RenderContext context = null;
057:
058: /**
059: * The cached value of the '{@link #getLayers() <em>Layers</em>}' reference list.
060: *
061: * @see #getLayers()
062: */
063: protected final SortedSet<Layer> layers = Collections
064: .synchronizedSortedSet(new TreeSet<Layer>());
065:
066: public RendererCreatorImpl() {
067: super ();
068: }
069:
070: public RenderContext getContext() {
071: return context;
072: }
073:
074: public void setContext(RenderContext newContext) {
075: context = newContext;
076: }
077:
078: /**
079: * <!-- begin-user-doc --> <!-- end-user-doc -->
080: *
081: * @uml.property name="layers"
082: * @generated NOT
083: */
084: @SuppressWarnings("unchecked")
085: public SortedSet<Layer> getLayers() {
086: return layers;
087: }
088:
089: /**
090: * <code>MetricsMap</code> maintains a list of metrics by Layer.
091: *
092: * @uml.property name="metrics"
093: * @uml.associationEnd qualifier="key:java.lang.Object java.util.SortedSet<IRenderMetrics>"
094: */
095: Map<Layer, List<InternalRenderMetrics>> layerToMetricsFactoryMap = new HashMap<Layer, List<InternalRenderMetrics>>();
096:
097: public Map<String, String> getAvailableRenderersInfo(Layer layer) {
098: Map<String, String> renderers = new HashMap<String, String>();
099:
100: List<InternalRenderMetrics> availableRenderers = layerToMetricsFactoryMap
101: .get(layer);
102:
103: for (InternalRenderMetrics irm : availableRenderers) {
104: renderers.put(irm.getName(), irm.getDescription());
105: }
106:
107: return renderers;
108: }
109:
110: /**
111: * <!-- begin-user-doc --> <!-- end-user-doc -->
112: *
113: * @generated NOT
114: */
115: public Renderer getRenderer(RenderContext context) {
116:
117: // Part 1 of decision goes here
118: Object o = layerToMetricsFactoryMap.get(context.getLayer());
119: if (o == null)
120: createConfiguration();
121: List<InternalRenderMetrics> list = layerToMetricsFactoryMap
122: .get(context.getLayerInternal());
123: if (list.isEmpty())
124: return getPlaceHolder(context);
125: InternalRenderMetrics internalRenderMetrics = null;
126: for (Iterator<InternalRenderMetrics> iter = list.iterator(); iter
127: .hasNext()
128: && internalRenderMetrics == null;) {
129: internalRenderMetrics = iter.next();
130: boolean canRender;
131: try {
132: canRender = internalRenderMetrics
133: .getRenderMetricsFactory().canRender(context);
134: if (canRender) {
135: Renderer createRenderer = internalRenderMetrics
136: .createRenderer();
137: createRenderer.setContext(context);
138: return createRenderer;
139: }
140: } catch (Throwable e) {
141: internalRenderMetrics = null;
142: }
143: }
144: return getPlaceHolder(context);
145: }
146:
147: private PlaceHolder getPlaceHolder(RenderContext context) {
148: PlaceHolder placeHolder = new PlaceHolder();
149: placeHolder.setContext(context);
150: return placeHolder;
151: }
152:
153: void createConfiguration() {
154:
155: // Because the rendermetrics may call any code in order to obtain their metrics it is possible
156: // that the rendermetrics could end up triggering createConfiguration to be called. Because of this
157: // the render metrics methods cannot be called within a synchronization block because a deadlock could occur
158: // Consider: rendermetrics somehow resets a IGeoResource which triggers a re-render (and therefore a re-evaluation of
159: // the renderers). If anywhere there is synchronous waiting between 2 threads a dead lock can occur.
160: // To overcome this issue this method has limitted synchronization. The configuration is made but before it assigns the
161: // configuration it determines whether or not the layers have changed since it started (this check is in a synchronization
162: // block so that it is thread safe) if the layers have changed then it starts over again.
163: // This way there is not change for deadlock but the correctness semantics are maintained.
164: boolean configurationPassed = false;
165:
166: while (!configurationPassed) {
167:
168: initRenderMetrics();
169:
170: Set<Layer> configured = new HashSet<Layer>();
171: List<Layer> layers = new ArrayList<Layer>();
172:
173: synchronized (this .layers) {
174: layers.addAll(this .layers);
175: }
176:
177: Map<Layer, RenderContext> configuration = new HashMap<Layer, RenderContext>();
178:
179: LAYERS: for (int i = 0; i < layers.size(); i++) {
180: Layer layer = layers.get(i);
181:
182: if (configured.contains(layer)) {
183: continue LAYERS;
184: }
185:
186: List<InternalRenderMetrics> layerfactories = layerToMetricsFactoryMap
187: .get(layer);
188: Collections.sort(layerfactories,
189: new RenderMetricsSorter(layers));
190:
191: if (layerfactories.isEmpty()) {
192: // nobody loves this layer
193: // layer.setStatus( Layer.UNCONFIGURED );
194: continue LAYERS;
195: } else {
196: IRenderMetrics metrics = layerfactories.get(0); // sorted in order of preference
197:
198: if (metrics != null) {
199: RenderContext renderContext = (RenderContext) metrics
200: .getRenderContext();
201: if (renderContext instanceof CompositeRenderContext) {
202: constructCompositeContext(
203: configured,
204: layers,
205: configuration,
206: i,
207: metrics,
208: (CompositeRenderContext) renderContext);
209: }
210: configuration.put(layer, renderContext);
211: }
212: }
213: }
214:
215: synchronized (this .layers) {
216: Iterator<Layer> iter1 = layers.iterator();
217: Iterator<Layer> iter2 = this .layers.iterator();
218: boolean failed = false;
219: while (iter1.hasNext()) {
220: if (!iter2.hasNext()) {
221: failed = true;
222: break;
223: }
224: if (!iter1.next().equals(iter2.next())) {
225: failed = true;
226: break;
227: }
228: }
229: if (!failed) {
230: this .configuration = Collections
231: .synchronizedMap(configuration);
232: configurationPassed = true;
233: }
234: }
235: }
236: }
237:
238: private void constructCompositeContext(Set<Layer> configured,
239: List<Layer> layers,
240: Map<Layer, RenderContext> configuration, int i,
241: IRenderMetrics metrics, CompositeRenderContext renderContext) {
242:
243: renderContext.addContexts(Collections.singleton(renderContext));
244: configured.add(renderContext.getLayerInternal());
245: configuration.put(renderContext.getLayerInternal(),
246: renderContext);
247:
248: CONTEXT: for (int j = i + 1; j < layers.size(); j++) {
249: try {
250: Layer layer = layers.get(j);
251: if (!configured.contains(layer)
252: && metrics.canAddLayer(layer)) {
253: addChildContextToComposite(configured,
254: configuration, renderContext, layer);
255: } else {
256: break CONTEXT;
257: }
258: } catch (Exception e) {
259: break CONTEXT;
260: }
261: }
262: }
263:
264: private void addChildContextToComposite(Set<Layer> configured,
265: Map<Layer, RenderContext> configuration,
266: CompositeRenderContext renderContext, Layer layer) {
267: List<InternalRenderMetrics> layerfactories2 = layerToMetricsFactoryMap
268: .get(layer);
269: IRenderMetrics metrics2 = layerfactories2.get(0);
270: Set<RenderContext> child = Collections
271: .singleton((RenderContext) metrics2.getRenderContext());
272: renderContext.addContexts(child);
273: // add to configurated to indicate that it has been configured.
274: configured.add(layer);
275:
276: configuration.put(layer, renderContext);
277: }
278:
279: /**
280: * Initialize all known render metrics for a given layer
281: */
282: private void initRenderMetrics() {
283: for (Layer layer : getLayers()) {
284: if (!layerToMetricsFactoryMap.containsKey(layer))
285: initFactories(layer);
286: }
287: }
288:
289: /**
290: * <!-- begin-user-doc --> <!-- end-user-doc -->
291: *
292: * @generated NOT
293: */
294: public RenderContext getRenderContext(Layer layer) {
295: if (configuration == null)
296: createConfiguration();
297: return configuration.get(layer);
298: }
299:
300: public void changed(Notification event) {
301: if (((event.getNotifier() instanceof ContextModel || event
302: .getNotifier() instanceof RenderManager) && event
303: .getFeatureID(ContextModel.class) == ProjectPackage.CONTEXT_MODEL__LAYERS)) {
304: handleMapCompositionEvent(event);
305: }
306: }
307:
308: @SuppressWarnings("unchecked")
309: private void handleMapCompositionEvent(Notification event) {
310: switch (event.getEventType()) {
311: case Notification.ADD: {
312: Layer layer = (Layer) event.getNewValue();
313: List<Layer> layers = new ArrayList<Layer>();
314: layers.add(layer);
315: if (layer.hasResource(FeatureSource.class))
316: layers.add(new SelectionLayer(layer));
317: getLayers().addAll(layers);
318: break;
319: }
320: case Notification.ADD_MANY: {
321: List<Layer> layers = new ArrayList<Layer>();
322: for (Layer layer : (Collection<? extends Layer>) event
323: .getNewValue()) {
324: layers.add(layer);
325: if (layer.hasResource(FeatureSource.class)
326: && findSelectionLayer(layer) == null)
327: layers.add(new SelectionLayer(layer));
328: }
329: getLayers().addAll(layers);
330: break;
331: }
332:
333: /*
334: * The collection <code>layers</code> is a sorted TreeMap of <? extends Layer> objects:
335: * Layer.compareTo() is used to sort and identify items for equality. Comparing is performed
336: * by z-order. But this collection (<code>layers</code>) contains also
337: * additional SelectionLayer objects and their z-order is artificial. This leads to
338: * errors during removing by TreeMap.remove(..) methods.
339: * The <code>layers</code> collection is re-created safely to fix deleting
340: * layers from map with synchronization of this cache list of layers and selection layers with
341: * map's list.
342: */
343:
344: case Notification.REMOVE: {
345:
346: synchronized (layers) {
347:
348: Layer removedLayer = (Layer) event.getOldValue();
349:
350: for (Iterator iter = layers.iterator(); iter.hasNext();) {
351: Layer l = (Layer) iter.next();
352: if (removedLayer == l)
353: iter.remove();
354: else if (l instanceof SelectionLayer) {
355: SelectionLayer sl = (SelectionLayer) l;
356: if (removedLayer == sl.getWrappedLayer())
357: iter.remove();
358: }
359: }
360:
361: }
362: break;
363: }
364: case Notification.REMOVE_MANY: {
365:
366: synchronized (layers) {
367: Collection<Layer> removedLayers = (Collection<Layer>) event
368: .getOldValue();
369:
370: for (Iterator iter = layers.iterator(); iter.hasNext();) {
371: Layer l = (Layer) iter.next();
372: if (removedLayers.contains(l))
373: iter.remove();
374: else if (l instanceof SelectionLayer) {
375: SelectionLayer sl = (SelectionLayer) l;
376: if (removedLayers
377: .contains(sl.getWrappedLayer()))
378: iter.remove();
379: }
380: }
381: }
382: break;
383: }
384: case Notification.MOVE: {
385: // this should be a layer accordint to the reverse engineered rules...
386: // I like type safety better. or at least documentation :(
387: Layer newV = (Layer) event.getNewValue();
388:
389: // remove then add the layers to fix ordering of layers.
390: synchronized (layers) {
391: SelectionLayer selectionLayer = null;
392: for (Iterator iter = layers.iterator(); iter.hasNext();) {
393: Layer l = (Layer) iter.next();
394: if (newV == l)
395: iter.remove();
396: else if (l instanceof SelectionLayer) {
397: SelectionLayer sl = (SelectionLayer) l;
398: if (newV == sl.getWrappedLayer()) {
399: iter.remove();
400: selectionLayer = sl;
401: }
402: }
403: }
404: layers.add(newV);
405: if (selectionLayer != null) {
406: layers.add(selectionLayer);
407: }
408: }
409:
410: break;
411: }
412: case Notification.SET: {
413: Layer oldV = (Layer) event.getOldValue();
414:
415: Layer newV = (Layer) event.getNewValue();
416: SelectionLayer selectionLayer = null;
417: if (newV.hasResource(FeatureSource.class))
418: selectionLayer = new SelectionLayer(newV);
419:
420: // remove then add the layers to fix ordering of layers.
421: synchronized (layers) {
422: for (Iterator iter = layers.iterator(); iter.hasNext();) {
423: Layer l = (Layer) iter.next();
424: if (oldV == l)
425: iter.remove();
426: else if (l instanceof SelectionLayer) {
427: SelectionLayer sl = (SelectionLayer) l;
428: if (oldV == sl.getWrappedLayer()) {
429: iter.remove();
430: }
431: }
432: }
433: layers.add(newV);
434: if (selectionLayer != null) {
435: layers.add(selectionLayer);
436: }
437: }
438:
439: break;
440: }
441: default:
442: break;
443: }
444: configuration = null;
445: }
446:
447: /**
448: * Locates the selection layer for layer or returns null;
449: *
450: * @return the selection layer for layer or returns null;
451: */
452: public SelectionLayer findSelectionLayer(ILayer targetLayer) {
453: try {
454: if (targetLayer.getResource(FeatureSource.class, null) == null)
455: return null;
456: } catch (IOException e) {
457: return null;
458: }
459: for (Layer layer : getLayers())
460: if (layer instanceof SelectionLayer)
461: if (((SelectionLayer) layer).getWrappedLayer() == targetLayer)
462: return (SelectionLayer) layer;
463:
464: return null;
465: }
466:
467: /**
468: * @uml.property name="configuration"
469: * @uml.associationEnd qualifier="key:java.lang.Object
470: * net.refractions.udig.project.internal.render.RenderContext"
471: */
472: private volatile Map<Layer, RenderContext> configuration;
473:
474: /**
475: * @author Jesse
476: * @since 1.0.0
477: */
478: static class DumbRendererMetrics implements IRenderMetrics {
479:
480: private RenderContext context;
481:
482: /**
483: * @param layer
484: */
485: public DumbRendererMetrics(Layer layer, IRenderContext context) {
486: this .context = (RenderContext) context;
487: }
488:
489: /**
490: * @see net.refractions.udig.project.render.IRenderMetrics#createRenderer()
491: */
492: public Renderer createRenderer() {
493: return new DumbRenderer();
494: }
495:
496: /**
497: * @see net.refractions.udig.project.render.IRenderMetrics#getRenderContext()
498: */
499: public RenderContext getRenderContext() {
500: return context;
501: }
502:
503: /**
504: * @see net.refractions.udig.project.render.IRenderMetrics#setRenderContext(net.refractions.udig.project.render.RenderContext)
505: */
506: public void setRenderContext(IRenderContext context) {
507: // do nothing;
508: }
509:
510: /**
511: * @see net.refractions.udig.project.render.IRenderMetrics#getRenderMetricsFactory()
512: */
513: public IRenderMetricsFactory getRenderMetricsFactory() {
514: return null;
515: }
516:
517: public boolean canAddLayer(ILayer layer) {
518: return false;
519: }
520:
521: public boolean canStyle(String SyleID, Object value) {
522: return false;
523: }
524:
525: public boolean isOptimized() {
526: return false;
527: }
528:
529: }
530:
531: static class DumbRenderer extends RendererImpl {
532:
533: /**
534: * @see net.refractions.udig.project.internal.render.impl.RendererImpl#render(java.awt.Graphics2D)
535: */
536: public void render(Graphics2D destination,
537: IProgressMonitor monitor) {
538: // do nothing
539: }
540:
541: /**
542: * @see net.refractions.udig.project.internal.render.impl.RendererImpl#getInfo(java.awt.Point)
543: * public InfoList getInfo( Point screenLocation ) { // do nothing return null; }
544: */
545:
546: /**
547: * @see net.refractions.udig.project.internal.render.impl.RendererImpl#stopRendering()
548: */
549: public void stopRendering() {
550: // do nothing
551: }
552:
553: /**
554: * @see net.refractions.udig.project.internal.render.impl.RendererImpl#dispose()
555: */
556: public void dispose() {
557: // do nothing
558: }
559:
560: /**
561: * @see net.refractions.udig.project.internal.render.impl.RendererImpl#render(com.vividsolutions.jts.geom.Envelope,
562: * org.eclipse.core.runtime.IProgressMonitor)
563: */
564: public void render(IProgressMonitor monitor) {
565: // do nothing
566: }
567:
568: }
569:
570: private int rate(IRenderMetrics factory) {
571: return 1;
572: }
573:
574: private void initFactories(Layer layer) {
575:
576: RendererExtensionProcessor p = new RendererExtensionProcessor(
577: layer, getContext().getMapInternal(), getContext()
578: .getRenderManagerInternal());
579:
580: ExtensionPointUtil.process(ProjectPlugin.getPlugin(),
581: IRenderer.RENDER_EXT, p);
582: layerToMetricsFactoryMap.put(layer, p.rFactories);
583:
584: }
585:
586: Collection<RenderContext> contexts = Collections
587: .synchronizedSet(new TreeSet<RenderContext>());
588:
589: /**
590: * @see net.refractions.udig.project.internal.render.RendererCreator#getConfiguration()
591: */
592: public Collection<RenderContext> getConfiguration() {
593:
594: if (configuration == null)
595: createConfiguration();
596:
597: Set<RenderContext> values;
598: synchronized (configuration) {
599: values = new HashSet<RenderContext>(configuration.values());
600:
601: }
602: synchronized (contexts) {
603: contexts.clear();
604: for (RenderContext context : values) {
605: contexts.add(context);
606: }
607: }
608: return new ArrayList<RenderContext>(contexts);
609: }
610:
611: public void reset() {
612: configuration = null;
613: contexts.clear();
614:
615: getConfiguration();
616: }
617:
618: } // RendererCreatorImpl
|