001: /*
002: * GeoTools - OpenSource mapping toolkit
003: * http://geotools.org
004: * (C) 2006, GeoTools Project Managment Committee (PMC)
005: * (C) 2005, Refractions Research Inc.
006: *
007: * This library is free software; you can redistribute it and/or
008: * modify it under the terms of the GNU Lesser General Public
009: * License as published by the Free Software Foundation;
010: * version 2.1 of the License.
011: *
012: * This library is distributed in the hope that it will be useful,
013: * but WITHOUT ANY WARRANTY; without even the implied warranty of
014: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
015: * Lesser General Public License for more details.
016: */
017: package org.geotools.catalog.defaults;
018:
019: import java.io.IOException;
020: import java.net.MalformedURLException;
021: import java.net.URI;
022: import java.net.URISyntaxException;
023: import java.net.URL;
024: import java.util.ArrayList;
025: import java.util.Collections;
026: import java.util.HashSet;
027: import java.util.Iterator;
028: import java.util.LinkedList;
029: import java.util.List;
030:
031: import org.geotools.catalog.AbstractCatalog;
032: import org.geotools.catalog.Catalog;
033: import org.geotools.catalog.CatalogInfo;
034: import org.geotools.catalog.GeoResource;
035: import org.geotools.catalog.GeoResourceInfo;
036: import org.geotools.catalog.ResolveChangeEvent;
037: import org.geotools.catalog.ResolveChangeListener;
038: import org.geotools.catalog.ResolveDelta;
039: import org.geotools.catalog.Service;
040: import org.geotools.catalog.ServiceInfo;
041: import org.geotools.util.ListenerList;
042: import org.geotools.util.ProgressListener;
043:
044: import com.vividsolutions.jts.geom.Envelope;
045:
046: /**
047: * Default Catalog implementation. All services are stored in memory.
048: *
049: * @author David Zwiers, Refractions Research
050: * @author Justin Deoliveira, jdeolive@openplans.org
051: * @since 0.6
052: * @source $URL: http://svn.geotools.org/geotools/tags/2.4.1/modules/library/main/src/main/java/org/geotools/catalog/defaults/DefaultCatalog.java $
053: */
054: public class DefaultCatalog extends AbstractCatalog {
055: private HashSet services = new HashSet();
056: private CatalogInfo metadata;
057: private ListenerList catalogListeners;
058:
059: public DefaultCatalog() {
060: DefaultCatalogInfo metadata = new DefaultCatalogInfo() {
061: };
062: metadata.setTitle("Default Catalog"); //$NON-NLS-1$
063: try {
064: metadata.setSource(new URI("http://localhost"));
065: } catch (URISyntaxException e) {
066: //do nothing
067: }
068:
069: this .metadata = metadata;
070: catalogListeners = new ListenerList();
071: }
072:
073: public DefaultCatalog(CatalogInfo metadata) {
074: this ();
075: this .metadata = metadata;
076: }
077:
078: /**
079: * @see net.refractions.udig.catalog.ICatalog#addCatalogListener(net.refractions.udig.catalog.ICatalog.ICatalogListener)
080: * @param listener
081: */
082: public void addCatalogListener(ResolveChangeListener listener) {
083: catalogListeners.add(listener);
084: }
085:
086: /**
087: * @see net.refractions.udig.catalog.ICatalog#removeCatalogListener(net.refractions.udig.catalog.ICatalog.ICatalogListener)
088: * @param listener
089: */
090: public void removeCatalogListener(ResolveChangeListener listener) {
091: catalogListeners.remove(listener);
092: }
093:
094: /**
095: * @see net.refractions.udig.catalog.ICatalog#add(net.refractions.udig.catalog.IService)
096: * @param entry
097: * @throws UnsupportedOperationException
098: */
099: public void add(Service entry) throws UnsupportedOperationException {
100: if (entry == null || entry.getIdentifier() == null)
101: throw new NullPointerException("Cannot have a null id"); //$NON-NLS-1$
102: services.add(entry);
103: ResolveDelta deltaAdded = new DefaultResolveDelta(entry,
104: ResolveDelta.Kind.ADDED);
105: ResolveDelta deltaChanged = new DefaultResolveDelta(this ,
106: Collections.singletonList(deltaAdded));
107: fire(new DefaultResolveChangeEvent(DefaultCatalog.this ,
108: ResolveChangeEvent.Type.POST_CHANGE, deltaChanged));
109: }
110:
111: /**
112: * @see net.refractions.udig.catalog.ICatalog#remove(net.refractions.udig.catalog.IService)
113: * @param entry
114: * @throws UnsupportedOperationException
115: */
116: public void remove(Service entry)
117: throws UnsupportedOperationException {
118: if (entry == null || entry.getIdentifier() == null)
119: throw new NullPointerException("Cannot have a null id"); //$NON-NLS-1$
120: ResolveDelta deltaRemoved = new DefaultResolveDelta(entry,
121: ResolveDelta.Kind.REMOVED);
122: ResolveDelta deltaChanged = new DefaultResolveDelta(this ,
123: Collections.singletonList(deltaRemoved));
124: fire(new DefaultResolveChangeEvent(DefaultCatalog.this ,
125: ResolveChangeEvent.Type.PRE_DELETE, deltaChanged));
126: services.remove(entry);
127: fire(new DefaultResolveChangeEvent(DefaultCatalog.this ,
128: ResolveChangeEvent.Type.POST_CHANGE, deltaRemoved));
129: }
130:
131: public void replace(URI id, Service entry)
132: throws UnsupportedOperationException {
133: if (entry == null || entry.getIdentifier() == null
134: || id == null)
135: throw new NullPointerException("Cannot have a null id"); //$NON-NLS-1$
136:
137: List victims = findService(id, null);
138:
139: List changes = new ArrayList();
140: for (Iterator vitr = victims.iterator(); vitr.hasNext();) {
141: Service service = (Service) vitr.next();
142: List childChanges = new ArrayList();
143: try {
144: List newChildren = entry.members(null);
145: List oldChildren = service.members(null);
146: if (oldChildren != null)
147: for (Iterator oitr = oldChildren.iterator(); oitr
148: .hasNext();) {
149: GeoResource oldChild = (GeoResource) oitr
150: .next();
151: String oldName = oldChild.getIdentifier()
152: .toString();
153: for (Iterator citr = newChildren.iterator(); citr
154: .hasNext();) {
155: GeoResource child = (GeoResource) citr
156: .next();
157: String name = child.getIdentifier()
158: .toString();
159: if (oldName.equals(name)) {
160: childChanges
161: .add(new DefaultResolveDelta(
162: child,
163: oldChild,
164: ResolveDelta.NO_CHILDREN));
165: break;
166: }
167: }
168: }
169: } catch (IOException ignore) {
170: // no children? Not a very good entry ..
171: }
172: changes.add(new DefaultResolveDelta(service, entry,
173: childChanges));
174: }
175: ResolveDelta deltas = new DefaultResolveDelta(this , changes);
176: ResolveChangeEvent event = new DefaultResolveChangeEvent(this ,
177: ResolveChangeEvent.Type.PRE_DELETE, deltas);
178: fire(event);
179:
180: services.removeAll(victims);
181: services.add(entry);
182: }
183:
184: /**
185: * Quick search by url match.
186: * @param query
187: *
188: * @see net.refractions.udig.catalog.ICatalog#search(org.geotools.filter.Filter)
189: * @return List<IResolve>
190: * @throws IOException
191: */
192: public List find(URI query, ProgressListener monitor) {
193: List list = new ArrayList();
194: try {
195: URL qurl = query.toURL();
196: for (Iterator itr = services.iterator(); itr.hasNext();) {
197: Service service = (Service) itr.next();
198: URL url = service.getIdentifier().toURL();
199: if (url.getProtocol().equalsIgnoreCase(
200: qurl.getProtocol())
201: && ((url.getHost() == null || "".equals(url.getHost())) || (url.getHost() != null && url.getHost().equalsIgnoreCase(qurl.getHost()))) && //$NON-NLS-1$
202: ((url.getPath() == null || "".equals(url.getPath())) || (url.getPath() != null && url.getPath().equalsIgnoreCase(qurl.getPath()))) && //$NON-NLS-1$
203: ((url.getQuery() == null || "".equals(url.getQuery())) || (url.getQuery() != null && url.getQuery().equalsIgnoreCase(qurl.getQuery()))) && //$NON-NLS-1$
204: ((url.getAuthority() == null || "".equals(url.getAuthority())) || (url.getAuthority() != null && url.getAuthority().equalsIgnoreCase(qurl.getAuthority())))) { //$NON-NLS-1$
205: Iterator i;
206: if (qurl.getRef() == null) {
207: list.add(service);
208: } else
209: try {
210:
211: /*
212: * Although the following is a 'blocking' call, we have deemed it safe based on
213: * the following reasons: This will only be called for Identifiers which are
214: * well known. The Services being checked have already been screened, and only a
215: * limited number of services (ussually 1) will be called. 1) The Id was aquired
216: * from the catalog ... and this is a look-up, in which case the uri exists. 2)
217: * The Id was persisted. In the future this will also be free, as we plan on
218: * caching the equivalent of a getCapabilities document between runs (will have
219: * to be updated too as the app has time).
220: */
221: List t = service.members(monitor);
222: i = t == null ? null : t.iterator();
223: if (qurl.getRef() != null) {
224: // it's a resource
225: while (i.hasNext()) {
226: GeoResource res = (GeoResource) i
227: .next();
228: if (qurl.getRef().equals(
229: res.getIdentifier().toURL()
230: .getRef()))
231: list.add(res);
232: }
233: } else {
234: while (i != null && i.hasNext()) {
235: list.add(i.next());
236: }
237: }
238:
239: } catch (IOException e) {
240: e.printStackTrace();
241: //TODO: log
242: }
243:
244: }
245: }
246: } catch (MalformedURLException e) {
247: e.printStackTrace();
248: //TODO: log
249: }
250: return list;
251: }
252:
253: /**
254: * Quick search by url match.
255: *
256: * @see net.refractions.udig.catalog.ICatalog#search(org.geotools.filter.Filter)
257: * @param query
258: * @return List<IResolve>
259: * @throws IOException
260: */
261: public List findService(URI query, ProgressListener monitor) {
262:
263: List list = new ArrayList();
264:
265: try {
266: URL qurl = query.toURL();
267: if (qurl.getRef() != null)
268: return list;
269: for (Iterator itr = services.iterator(); itr.hasNext();) {
270: Service service = (Service) itr.next();
271:
272: URL url = service.getIdentifier().toURL();
273: if (url.getProtocol().equalsIgnoreCase(
274: qurl.getProtocol())
275: && ((url.getHost() == null || "".equals(url.getHost())) || (url.getHost() != null && url.getHost().equalsIgnoreCase(qurl.getHost()))) && //$NON-NLS-1$
276: ((url.getPath() == null || "".equals(url.getPath())) || (url.getPath() != null && url.getPath().equalsIgnoreCase(qurl.getPath()))) && //$NON-NLS-1$
277: ((url.getQuery() == null || "".equals(url.getQuery())) || (url.getQuery() != null && url.getQuery().equalsIgnoreCase(qurl.getQuery()))) && //$NON-NLS-1$
278: ((url.getAuthority() == null || "".equals(url.getAuthority())) || (url.getAuthority() != null && url.getAuthority().equalsIgnoreCase(qurl.getAuthority())))) { //$NON-NLS-1$
279: list.add(service);
280: }
281:
282: }
283: } catch (MalformedURLException e) {
284: //TODO: log this
285: e.printStackTrace();
286: }
287: return list;
288: }
289:
290: /**
291: * Performs a search on this catalog based on the specified inputs. The pattern uses the
292: * following conventions: use " " to surround a phase use + to represent 'AND' use - to
293: * represent 'OR' use ! to represent 'NOT' use ( ) to designate scope The bbox provided shall be
294: * in Lat - Long, or null if the search is not to be contained within a specified area.
295: *
296: * @see net.refractions.udig.catalog.ICatalog#search(java.lang.String,
297: * com.vividsolutions.jts.geom.Envelope)
298: * @param pattern
299: * @param bbox used for an intersection test
300: */
301: public synchronized List search(String pattern, Envelope bbox,
302: ProgressListener monitor) {
303:
304: if ((pattern == null || "".equals(pattern.trim()))
305: && (bbox == null || bbox.isNull())) //$NON-NLS-1$
306: return new LinkedList();
307:
308: AST ast = null;
309: if (pattern != null && !"".equals(pattern.trim()))
310: ast = ASTFactory.parse(pattern);
311:
312: // TODO check cuncurrency issues here
313:
314: List result = new LinkedList();
315: HashSet tmp = new HashSet();
316: tmp.addAll(this .services);
317: try {
318:
319: Iterator services = tmp.iterator();
320: if (services != null) {
321: while (services.hasNext()) {
322: Service service = (Service) services.next();
323: if (check(service, ast)) {
324: result.add(service);
325: }
326: Iterator resources;
327:
328: try {
329: List t = service.members(monitor);
330: resources = t == null ? null : t.iterator();
331: while (resources != null && resources.hasNext()) {
332: GeoResource resource = (GeoResource) resources
333: .next();
334: if (check(resource, ast, bbox)) {
335: result.add(resource);
336: }
337: }
338: } catch (IOException e) {
339: //TODO log this
340: } finally {
341:
342: }
343: }
344: }
345: return result;
346: } finally {
347:
348: }
349: }
350:
351: /* check the fields we catre about */
352: protected static boolean check(Service service, AST pattern) {
353: if (pattern == null) {
354: return false;
355: }
356: ServiceInfo info;
357: try {
358: info = service == null ? null : service.getInfo(null);
359: } catch (IOException e) {
360: info = null;
361: e.printStackTrace();
362: //TODO: log this
363: }
364: boolean t = false;
365: if (info != null) {
366: if (info.getTitle() != null)
367: t = pattern.accept(info.getTitle());
368: if (!t && info.getKeywords() != null) {
369: String[] keys = info.getKeywords();
370: for (int i = 0; !t && i < keys.length; i++)
371: if (keys[i] != null)
372: t = pattern.accept(keys[i]);
373: }
374: if (!t && info.getSchema() != null)
375: t = pattern.accept(info.getSchema().toString());
376: if (!t && info.getAbstract() != null)
377: t = pattern.accept(info.getAbstract());
378: if (!t && info.getDescription() != null)
379: t = pattern.accept(info.getDescription());
380: }
381: return t;
382: }
383:
384: /* check the fields we catre about */
385: protected static boolean check(GeoResource resource, AST pattern) {
386: if (pattern == null)
387: return true;
388: GeoResourceInfo info;
389: try {
390: info = (resource == null ? null : resource.getInfo(null));
391: } catch (IOException e) {
392: //TODO: log this
393: info = null;
394: e.printStackTrace();
395: }
396: boolean t = false;
397: if (info != null) {
398: if (info.getTitle() != null)
399: t = pattern.accept(info.getTitle());
400: if (!t && info.getName() != null)
401: t = pattern.accept(info.getName());
402: if (!t && info.getKeywords() != null) {
403: String[] keys = info.getKeywords();
404: for (int i = 0; !t && i < keys.length; i++)
405: if (keys[i] != null)
406: t = pattern.accept(keys[i]);
407: }
408: if (!t && info.getSchema() != null)
409: t = pattern.accept(info.getSchema().toString());
410: if (!t && info.getDescription() != null)
411: t = pattern.accept(info.getDescription());
412: }
413: return t;
414: }
415:
416: protected static boolean check(GeoResource resource, AST pattern,
417: Envelope bbox) {
418: if (!check(resource, pattern))
419: return false;
420: if (bbox == null || bbox.isNull())
421: return true; // no checking here
422: try {
423: return bbox.intersects(resource.getInfo(null).getBounds());
424: } catch (IOException e) {
425: //TODO: log this
426: e.printStackTrace();
427: return false;
428: }
429: }
430:
431: /**
432: * Fire a resource changed event, these may be batched into one delta for performance.
433: *
434: * @param event the event to be fired
435: *
436: * @throws IOException protected void fireResourceEvent( IGeoResource resource,
437: * IResolveDelta.Kind kind ) throws IOException { Object[] listeners =
438: * catalogListeners.getListeners(); if( listeners.length == 0 ) return;
439: * GeoReferenceDelta rDelta = new GeoReferenceDelta( resource, kind ); ServiceDelta
440: * sDelta = new ServiceDelta( resource.getService(null), IDelta.Kind.NO_CHANGE,
441: * Collections.singletonList( rDelta ) ); CatalogDelta cDelta = new CatalogDelta(
442: * Collections.singletonList( (IDelta)sDelta ) ); fire( new CatalogChangeEvent(
443: * resource, ICatalogChangeEvent.Type.POST_CHANGE, cDelta ) ); }
444: */
445:
446: public void fire(ResolveChangeEvent event) {
447: Object[] listeners = catalogListeners.getListeners();
448: if (listeners.length == 0)
449: return;
450:
451: for (int i = 0; i < listeners.length; ++i) {
452: try {
453: ((ResolveChangeListener) listeners[i]).changed(event);
454: } catch (Throwable die) {
455: die.printStackTrace();
456: //TODO: log this
457: }
458: }
459: }
460:
461: /*
462: * protected void fireServiceChanged( Object source, ServiceDelta sDelta ) { Object[] listeners =
463: * catalogListeners.getListeners(); if( listeners.length == 0 ) return; CatalogDelta cDelta =
464: * new CatalogDelta( Collections.singletonList( (IDelta)sDelta ) ); fire( new
465: * CatalogChangeEvent( source , ICatalogChangeEvent.Type.POST_CHANGE, cDelta ) ); }
466: */
467:
468: /**
469: * @see net.refractions.udig.catalog.ICatalog#resolve(java.lang.Class,
470: * org.eclipse.core.runtime.IProgressMonitor)
471: */
472: public Object resolve(Class adaptee, ProgressListener monitor) {
473:
474: if (adaptee == null)
475: return null;
476: if (adaptee.isAssignableFrom(Catalog.class))
477: return this ;
478: if (adaptee.isAssignableFrom(CatalogInfo.class))
479: return metadata;
480: if (adaptee.isAssignableFrom(services.getClass()))
481: return services;
482: if (adaptee.isAssignableFrom(List.class))
483: return new LinkedList(services);
484: if (adaptee.isAssignableFrom(catalogListeners.getClass()))
485: return catalogListeners;
486:
487: return null;
488: }
489:
490: /*
491: * @see net.refractions.udig.catalog.IResolve#canResolve(java.lang.Class)
492: */
493: public boolean canResolve(Class adaptee) {
494: Object value = resolve(adaptee, null);
495: return value != null;
496: }
497:
498: /*
499: * @see net.refractions.udig.catalog.IResolve#members(org.eclipse.core.runtime.IProgressMonitor)
500: */
501: public List members(ProgressListener monitor) {
502: return new LinkedList(services);
503: }
504:
505: /*
506: * @see net.refractions.udig.catalog.IResolve#getStatus()
507: */
508: public Status getStatus() {
509: return Status.CONNECTED;
510: }
511:
512: /*
513: * @see net.refractions.udig.catalog.IResolve#getMessage()
514: */
515: public Throwable getMessage() {
516: return null;
517: }
518:
519: /*
520: * @see net.refractions.udig.catalog.IResolve#getIdentifier()
521: */
522: public URI getIdentifier() {
523: return metadata.getSource();
524: }
525:
526: // static class DefaultCatalogInfo extends CatalogInfo {
527: //
528: // DefaultCatalogInfo() {
529: // super(null, null, null, null);
530: // }
531: //
532: // /**
533: // * Construct <code>CatalogInfoImpl</code>.
534: // *
535: // * @param title
536: // * @param description
537: // * @param source
538: // * @param keywords
539: // */
540: // public DefaultCatalogInfo( String title, String description, URI source, String[] keywords ) {
541: // super(title, description, source, keywords);
542: // }
543: // /**
544: // * @param desc The desc to set.
545: // */
546: // void setDesc( String desc ) {
547: // this.description = desc;
548: // }
549: // /**
550: // * @param keywords The keywords to set.
551: // */
552: // void setKeywords( String[] keywords ) {
553: // this.keywords = keywords;
554: // }
555: // /**
556: // * @param source The source to set.
557: // */
558: // void setSource( URI source ) {
559: // this.source = source;
560: // }
561: // /**
562: // * @param title The title to set.
563: // */
564: // void setTitle( String title ) {
565: // this.title = title;
566: // }
567: // }
568: }
|