001: /*
002: * uDig - User Friendly Desktop Internet GIS client
003: * http://udig.refractions.net
004: * (C) 2004, Refractions Research Inc.
005: *
006: * This library is free software; you can redistribute it and/or
007: * modify it under the terms of the GNU Lesser General Public
008: * License as published by the Free Software Foundation;
009: * version 2.1 of the License.
010: *
011: * This library is distributed in the hope that it will be useful,
012: * but WITHOUT ANY WARRANTY; without even the implied warranty of
013: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
014: * Lesser General Public License for more details.
015: *
016: */
017: package net.refractions.udig.project.ui.internal;
018:
019: import java.awt.Color;
020: import java.io.File;
021: import java.io.IOException;
022: import java.net.MalformedURLException;
023: import java.net.URL;
024: import java.security.InvalidParameterException;
025: import java.util.ArrayList;
026: import java.util.Collections;
027: import java.util.List;
028:
029: import net.refractions.udig.catalog.CatalogPlugin;
030: import net.refractions.udig.catalog.IGeoResource;
031: import net.refractions.udig.catalog.IResolve;
032: import net.refractions.udig.catalog.IService;
033: import net.refractions.udig.catalog.IServiceFactory;
034: import net.refractions.udig.catalog.internal.ui.ResourceSelectionPage;
035: import net.refractions.udig.project.ProjectBlackboardConstants;
036: import net.refractions.udig.project.internal.Layer;
037: import net.refractions.udig.project.internal.LayerFactory;
038: import net.refractions.udig.project.internal.Map;
039: import net.refractions.udig.project.internal.Project;
040: import net.refractions.udig.project.internal.ProjectFactory;
041: import net.refractions.udig.project.internal.ProjectPlugin;
042: import net.refractions.udig.project.preferences.PreferenceConstants;
043: import net.refractions.udig.project.ui.ApplicationGIS;
044: import net.refractions.udig.project.ui.UDIGEditorInput;
045: import net.refractions.udig.ui.ExceptionDisplayer;
046: import net.refractions.udig.ui.PlatformGIS;
047:
048: import org.eclipse.core.runtime.IProgressMonitor;
049: import org.eclipse.core.runtime.IStatus;
050: import org.eclipse.core.runtime.NullProgressMonitor;
051: import org.eclipse.core.runtime.Status;
052: import org.eclipse.core.runtime.jobs.Job;
053: import org.eclipse.jface.preference.IPreferenceStore;
054: import org.eclipse.jface.preference.PreferenceConverter;
055: import org.eclipse.jface.wizard.Wizard;
056: import org.eclipse.jface.wizard.WizardDialog;
057: import org.eclipse.swt.graphics.RGB;
058: import org.eclipse.swt.widgets.Display;
059: import org.eclipse.ui.PlatformUI;
060:
061: /**
062: * A Factory for creating maps from different types of resources.
063: *
064: * @author jeichar
065: * @since 0.9.0
066: *
067: * @deprecated use {@link ApplicationGIS}
068: */
069: public class MapFactory {
070:
071: /**
072: * The maximum number of resources per service that can be added to a map without asking the
073: * user for permission. TODO expose this as a preference
074: */
075: public static final int MAX_RESOURCES_IN_SERVICE = 1;
076:
077: private static final MapFactory INSTANCE = new MapFactory();
078:
079: private MapFactory() {
080: // do nothing
081: }
082:
083: /**
084: * For each URL in <code>resources</code>, load the services at that location and access
085: * their layers. Then add the layers to the current map, or a new one if it doesn't exist. The
086: * new map will be created in the current project.
087: * <p>
088: * This is equivalent to calling <code>processURLs(resources, null)</code>.
089: * </p>
090: *
091: * @param resources a List of URLs pointing to services (WMS, Shapefile, etc)
092: * @deprecated use {@link ApplicationGIS#createAndOpenMap(List)}
093: */
094: public void processURLs(List<URL> resources) {
095: processURLs(resources, null);
096: }
097:
098: /**
099: * For each URL in <code>resources</code>, load the services at that location and access
100: * their layers.
101: * <p>
102: * The layers will then be added to the current map, if it exists, otherwise, a new map will be
103: * created in the project designated by <code>target</code>.
104: * </p>
105: * <p>
106: * This is equivalent to calling <code>processURLs(resources, target, false)</code>.
107: * </p>
108: *
109: * @param resources a List of URLs pointing to services (WMS, Shapefile, etc)
110: * @param target Project to use if a new map is going to be created
111: * @deprecated use {@link ApplicationGIS#createAndOpenMap(List, net.refractions.udig.project.IProject)}
112: */
113: public void processURLs(List<URL> resources, Project target) {
114: processURLs(resources, target, false);
115: }
116:
117: /**
118: * For each URL in <code>resources</code>, load the services at that location and access
119: * their layers.
120: * <p>
121: * If <code>newMap</code> is set to <code>true</code> or there is no current map, the layers
122: * will be added to a new map, contained in the project designated by <code>target</code>, or
123: * the current project if <code>target</code> is null.
124: * </p>
125: *
126: * @param resources a List of URLs pointing to services (WMS, Shapefile, etc)
127: * @param target Project to use if a new map is going to be created
128: * @param newMap if true, a new map will be created even if there is one already open
129: * @deprecated use {@link ApplicationGIS#addLayersToMap(net.refractions.udig.project.IProject, List)} or {@link ApplicationGIS#createAndOpenMap(List, net.refractions.udig.project.IProject)}
130: *
131: */
132: public void processURLs(List<URL> resources, Project target,
133: boolean newMap) {
134: process(target, resources, newMap);
135: }
136:
137: /**
138: * For each IResolve in <code>resources</code>, load the services at that location and access
139: * their layers.
140: * <p>
141: * If <code>newMap</code> is set to <code>true</code> or there is no current map, the layers
142: * will be added to a new map, contained in the project designated by <code>target</code>, or
143: * the current project if <code>target</code> is null.
144: * </p>
145: *
146: * @param resources a List of IResolves to load onto a map
147: * @param target Project to use if a new map is going to be created
148: * @param newMap if true, a new map will be created even if there is one already open
149: */
150: public void processResolves(List<IResolve> resources,
151: Project target, boolean newMap) {
152: process(target, resources, newMap);
153: }
154:
155: /**
156: * This method will perform all of its work in a Job and will not block.
157: * <p>
158: * TODO fix this Process a list of X, try and make
159: *
160: * @param resources a List of IResolves
161: * @param newMap forces a new map to be created
162: */
163: public void process(final Project target, final List resources,
164: final boolean newMap) {
165: if (resources == null) {
166: throw new InvalidParameterException(
167: "Parameter 'resources' cannot be null."); //$NON-NLS-1$
168: }
169:
170: Job job = new Job(
171: Messages.ProjectUIPlugin_loadingServices_title) {
172: @SuppressWarnings("unchecked")
173: protected IStatus run(final IProgressMonitor monitor) {
174:
175: List<Throwable> exceptions = new ArrayList<Throwable>();
176:
177: List<IResolve> services = new ArrayList<IResolve>();
178: List<IGeoResource> geoResources = new ArrayList<IGeoResource>();
179: List<Layer> layers = new ArrayList<Layer>();
180:
181: for (Object object : resources) {
182: if (monitor.isCanceled()) {
183: return Status.CANCEL_STATUS;
184: }
185: monitor.beginTask(Messages.MapFactory_taskSorting,
186: resources.size());
187: try {
188: if (object instanceof URL) {
189: services.addAll(handleURL((URL) object,
190: monitor));
191: } else if (object instanceof File) {
192: try {
193: services.addAll(handleURL(
194: ((File) object).toURL(),
195: monitor));
196: } catch (MalformedURLException e) {
197: // ignore non-URL strings
198: }
199: } else if (object instanceof String) {
200: try {
201: services.addAll(handleURL(new URL(
202: (String) object), monitor));
203: } catch (MalformedURLException e) {
204: // ignore non-URL strings
205: }
206: } else if (object instanceof IService) {
207: services.add((IService) object);
208: } else if (object instanceof IGeoResource) {
209: geoResources.add((IGeoResource) object);
210: } else if (object instanceof Layer) {
211: layers.add((Layer) object);
212: } else if (object instanceof java.util.Map) {
213: services.addAll(CatalogPlugin.getDefault()
214: .getServiceFactory().acquire(
215: (java.util.Map) object));
216: }
217: } catch (IOException e) {
218: exceptions.add(e);
219: ProjectUIPlugin.log(null, e);
220: }
221:
222: monitor.worked(1);
223: }
224:
225: List<IResolve> unChosenServices = new ArrayList<IResolve>();
226:
227: for (IResolve resolve : services) {
228: monitor
229: .beginTask(
230: Messages.ProjectUIPlugin_loadingServices_task,
231: services.size());
232: if (monitor.isCanceled()) {
233: return Status.CANCEL_STATUS;
234: }
235: try {
236: List<IGeoResource> resources = handleResolve(
237: resolve, monitor);
238:
239: if (resources.size() > MAX_RESOURCES_IN_SERVICE) {
240: unChosenServices.add(resolve);
241: } else {
242: geoResources.addAll(resources);
243: }
244: } catch (IOException e) {
245: exceptions.add(e);
246: ProjectUIPlugin.log(null, e);
247: }
248: monitor.worked(1);
249: }
250:
251: if (unChosenServices.size() > 0) {
252: geoResources
253: .addAll(getResourcesFromUser(unChosenServices));
254: }
255:
256: if (monitor.isCanceled()) {
257: return Status.CANCEL_STATUS;
258: }
259:
260: Map map = new CurrentMapFinder().getCurrentMap();
261: boolean mapExists = (map != null); // this is used later, and I don't understand
262: // what for!
263: if (map == null || newMap) {
264: map = getMap(monitor, target, newMap);
265: }
266:
267: if (map
268: .getBlackboard()
269: .get(
270: ProjectBlackboardConstants.MAP__BACKGROUND_COLOR) == null) {
271: IPreferenceStore store = ProjectPlugin.getPlugin()
272: .getPreferenceStore();
273: RGB background = PreferenceConverter.getColor(
274: store, PreferenceConstants.P_BACKGROUND);
275: map
276: .getBlackboard()
277: .put(
278: ProjectBlackboardConstants.MAP__BACKGROUND_COLOR,
279: new Color(background.red,
280: background.green,
281: background.blue));
282: }
283:
284: if (monitor.isCanceled()) {
285: return Status.CANCEL_STATUS;
286: }
287:
288: LayerFactory factory = map.getLayerFactory();
289: if (factory == null) {
290: factory = ProjectFactory.eINSTANCE
291: .createLayerFactory();
292: }
293:
294: if (monitor.isCanceled()) {
295: return Status.CANCEL_STATUS;
296: }
297:
298: for (IGeoResource resource : geoResources) {
299: monitor.beginTask(Messages.MapFactory_retrieveTask,
300: geoResources.size());
301: if (monitor.isCanceled()) {
302: return Status.CANCEL_STATUS;
303: }
304:
305: try {
306: IService service = resource.service(monitor);
307: CatalogPlugin.getDefault().getLocalCatalog()
308: .add(service);
309:
310: Layer layer = factory.createLayer(resource);
311: if (layer != null) {
312: layers.add(layer);
313: }
314: } catch (IOException e) {
315: exceptions.add(e);
316: ProjectUIPlugin.log(null, e);
317: }
318: monitor.worked(1);
319: }
320: if (!layers.isEmpty())
321: map.getLayersInternal().addAll(layers);
322: if (map.getLayersInternal().size() > 0
323: || newMap == true) {
324: ProjectExplorer.getProjectExplorer().open(map);
325: } else if (!mapExists) {
326: // this is very ambigious? what is going on here?
327: map.getProjectInternal().getElementsInternal()
328: .remove(map);
329: }
330:
331: if (exceptions.size() != 0) {
332: String message = null;
333: if (exceptions.size() > 1) {
334: message = Messages.MapFactory_multiError;
335: } else {
336: message = Messages.MapFactory_error;
337: }
338:
339: ExceptionDisplayer.displayExceptions(exceptions,
340: message, ProjectUIPlugin.ID);
341: }
342: monitor.done();
343:
344: return new Status(IStatus.OK, ProjectUIPlugin.ID,
345: IStatus.OK, Messages.ProjectUIPlugin_success,
346: null);
347: }
348: };
349: job.schedule();
350: }
351:
352: private List<IGeoResource> getResourcesFromUser(
353: List<IResolve> unChosenServices) {
354: final ResourceSelectionPage page = new ResourceSelectionPage(
355: Messages.ProjectUIPlugin_resourceSelectionPage_title,
356: null);
357: page.setResources(unChosenServices, null);
358: final List<IGeoResource> chosenResources = new ArrayList<IGeoResource>();
359:
360: PlatformGIS.syncInDisplayThread(new Runnable() {
361: public void run() {
362: Wizard wizard = new Wizard() {
363: public void addPages() {
364: addPage(page);
365: }
366:
367: @Override
368: public boolean performFinish() {
369: List<Object> list = page.getCheckedElements();
370: for (Object object : list) {
371: if (object instanceof IGeoResource)
372: chosenResources
373: .add((IGeoResource) object);
374: }
375: return true;
376: }
377:
378: };
379: WizardDialog dialog = new WizardDialog(Display
380: .getDefault().getActiveShell(), wizard);
381: dialog.setBlockOnOpen(true);
382: dialog.open();
383: }
384: });
385:
386: return chosenResources;
387: }
388:
389: private List<IGeoResource> handleResolve(IResolve resolve,
390: IProgressMonitor monitor) throws IOException {
391: return resources(resolve.members(monitor));
392: }
393:
394: private List<IService> handleURL(URL url, IProgressMonitor monitor)
395: throws IOException {
396: if (url.getFile().toLowerCase().endsWith(".udig")) { //$NON-NLS-1$
397: handleProjectURL(url, monitor);
398: return Collections.<IService> emptyList();
399: }
400:
401: // Process URL (we are expecting a IService)
402: List<IService> goodServices = acquireGoodServices(url, monitor);
403: if (goodServices.isEmpty()) {
404: throw new IOException("No service available for " + url); //$NON-NLS-1$
405: } else {
406: return goodServices;
407: }
408: }
409:
410: private void handleProjectURL(URL url, IProgressMonitor monitor) {
411: monitor = validateMonitor(monitor);
412: monitor.subTask(Messages.ProjectUIPlugin_loadingProject_task);
413:
414: ProjectPlugin.getPlugin().getProjectRegistry().getProject(
415: url.getPath());
416: return;
417: }
418:
419: /**
420: * Same as processResources(monitor, resources, null);
421: *
422: * @param monitor
423: * @param resources
424: * @return
425: */
426: public List<Layer> processResources(IProgressMonitor monitor,
427: List<Object> resources) {
428: return processResources(monitor, resources, null);
429: }
430:
431: /**
432: * Will acquire services for a single URL, as long as one service works we don't have an error.
433: * <p>
434: * If no servies work we will just punt out the exception from the last entry.
435: * </p>
436: *
437: * @param url
438: * @return
439: * @throws IOException
440: */
441: List<IService> acquireGoodServices(URL url, IProgressMonitor monitor)
442: throws IOException {
443: if (url == null)
444: return Collections.<IService> emptyList();
445: IServiceFactory factory = CatalogPlugin.getDefault()
446: .getServiceFactory();
447: List<IService> result = factory.acquire(url);
448: if (result.isEmpty()) {
449: throw new IOException(
450: "Could not acquire a working service for " + url); //$NON-NLS-1$
451: }
452: List<IService> good = new ArrayList<IService>();
453: IOException notGood = null;
454: for (IService service : result) {
455:
456: try {
457: if (service.members(monitor) != null) {
458: good.add(service);
459: }
460: } catch (IOException bad) {
461: notGood = bad;
462: }
463: }
464: if (good.isEmpty()) {
465: if (notGood != null) {
466: throw notGood;
467: }
468: throw new IOException(
469: "Could not acquire a working service for " + url); //$NON-NLS-1$
470: }
471:
472: return good;
473: }
474:
475: /**
476: * Processes each element within the list of resources and returns all the Layers that have been
477: * discovered as a result. This is typically used to turn URLs, Files, IServices and
478: * IGeoResources into Layers.
479: *
480: * @param monitor a progress monitor to indicate a user of work, can be null
481: * @param resources a list of objects to be processed
482: * @param target a target indicating that this is being performed on some object, can be null
483: * @return a List <Layer>containing all discovered layers
484: */
485: @SuppressWarnings("unchecked")
486: public List<Layer> processResources(IProgressMonitor monitor,
487: List<Object> resources, Object target) {
488:
489: monitor = validateMonitor(monitor);
490:
491: List<Layer> layers = new ArrayList<Layer>();
492: List<IResolve> services = new ArrayList<IResolve>();
493: List<IGeoResource> georesources = new ArrayList<IGeoResource>();
494:
495: if (resources.isEmpty())
496: return layers;
497:
498: for (Object object : resources) {
499: if (monitor.isCanceled())
500: return null;
501: if (object instanceof Layer) {
502: layers.add((Layer) object);
503: }
504: if (object instanceof IService) {
505: services.add((IService) object);
506: } else if (object instanceof IGeoResource) {
507: georesources.add((IGeoResource) object);
508: } else if (object instanceof java.util.Map) {
509: services.addAll(CatalogPlugin.getDefault()
510: .getServiceFactory().acquire(
511: (java.util.Map) object));
512: }
513: }
514:
515: return layers;
516: }
517:
518: List<IGeoResource> resources(List<? extends IResolve> resolveList)
519: throws IOException {
520: List<IGeoResource> build = new ArrayList<IGeoResource>();
521: for (IResolve resolve : resolveList) {
522: if (resolve instanceof IGeoResource) {
523: build.add((IGeoResource) resolve);
524: } else {
525: build.addAll(resources(resolve.members(null)));
526: }
527: }
528: return build;
529: }
530:
531: public Map getMap(IProgressMonitor monitor, Project project2,
532: boolean createMap) {
533:
534: Map map = null;
535: if (!createMap) {
536: map = new CurrentMapFinder().getCurrentMap();
537: }
538: if (map != null) {
539: return map;
540: }
541: Project project = project2;
542: if (project == null) {
543: project = ProjectPlugin.getPlugin().getProjectRegistry()
544: .getCurrentProject();
545: }
546: if (project == null) {
547: project = ProjectPlugin.getPlugin().getProjectRegistry()
548: .getDefaultProject();
549: }
550:
551: String mapName = getNewMapName(project);
552:
553: Map newmap = ProjectFactory.eINSTANCE.createMap(project,
554: mapName, new ArrayList<Layer>());
555:
556: return newmap;
557:
558: }
559:
560: private static class CurrentMapFinder implements Runnable {
561: Map map = null;
562:
563: /**
564: * @return
565: */
566: Map getCurrentMap() {
567: map = null;
568: PlatformGIS.syncInDisplayThread(this );
569: return map;
570: }
571:
572: /*
573: * (non-Javadoc)
574: *
575: * @see java.lang.Runnable#run()
576: */
577: public void run() {
578: // TODO Auto-generated method stub
579: if (isMapOpen()) {
580: UDIGEditorInput input = (UDIGEditorInput) PlatformUI
581: .getWorkbench().getActiveWorkbenchWindow()
582: .getActivePage().getActiveEditor()
583: .getEditorInput();
584: map = (Map) input.getProjectElement();
585: }
586: }
587:
588: boolean isMapOpen() {
589:
590: if (PlatformUI.getWorkbench().getActiveWorkbenchWindow()
591: .getActivePage().getActiveEditor() != null) {
592: if (PlatformUI.getWorkbench()
593: .getActiveWorkbenchWindow().getActivePage()
594: .getActiveEditor() instanceof MapEditor) {
595: return true;
596: }
597: }
598: return false;
599: }
600: }
601:
602: private String getNewMapName(Project currentProject) {
603: String name = Messages.ProjectUIPlugin_newMap_name;
604:
605: int count = currentProject.getElementsInternal().size() + 1;
606:
607: return name + count;
608: }
609:
610: IProgressMonitor validateMonitor(IProgressMonitor monitor) {
611: if (monitor == null) {
612: return new NullProgressMonitor();
613: }
614: return monitor;
615: }
616:
617: public static MapFactory instance() {
618: return INSTANCE;
619: }
620: }
|