Project

General

Profile

Download (21.8 KB) Statistics
| Branch: | Tag: | Revision:
1
package org.cybertaxonomy.utis.checklist;
2

    
3
import java.io.PrintStream;
4
import java.net.URI;
5
import java.util.ArrayList;
6
import java.util.EnumSet;
7
import java.util.Iterator;
8
import java.util.List;
9

    
10
import org.apache.lucene.queryParser.QueryParser;
11
import org.cybertaxonomy.utis.client.ServiceProviderInfo;
12
import org.cybertaxonomy.utis.query.TinkerPopClient;
13
import org.cybertaxonomy.utis.store.Neo4jStore;
14
import org.cybertaxonomy.utis.store.Neo4jStoreManager;
15
import org.cybertaxonomy.utis.tnr.msg.Classification;
16
import org.cybertaxonomy.utis.tnr.msg.NameType;
17
import org.cybertaxonomy.utis.tnr.msg.Query;
18
import org.cybertaxonomy.utis.tnr.msg.Query.Request;
19
import org.cybertaxonomy.utis.tnr.msg.Response;
20
import org.cybertaxonomy.utis.tnr.msg.Source;
21
import org.cybertaxonomy.utis.tnr.msg.Synonym;
22
import org.cybertaxonomy.utis.tnr.msg.Taxon;
23
import org.cybertaxonomy.utis.tnr.msg.TaxonBase;
24
import org.cybertaxonomy.utis.tnr.msg.TaxonName;
25
import org.cybertaxonomy.utis.tnr.msg.TnrMsg;
26
import org.cybertaxonomy.utis.utils.IdentifierUtils;
27
import org.cybertaxonomy.utis.utils.Profiler;
28
import org.cybertaxonomy.utis.utils.TnrMsgUtils;
29
import org.neo4j.graphdb.Relationship;
30

    
31
import com.tinkerpop.blueprints.Graph;
32
import com.tinkerpop.blueprints.Vertex;
33
import com.tinkerpop.blueprints.impls.neo4j2.Neo4j2Vertex;
34
import com.tinkerpop.blueprints.oupls.sail.GraphSail;
35
import com.tinkerpop.gremlin.java.GremlinPipeline;
36
import com.tinkerpop.pipes.util.FastNoSuchElementException;
37
import com.tinkerpop.pipes.util.structures.Table;
38

    
39
public class EEA_BDC_Client extends AggregateChecklistClient<TinkerPopClient> implements UpdatableStoreInfo {
40

    
41
    /**
42
     *
43
     */
44
    public static final String ID = "eea_bdc";
45
    public static final String LABEL = "European Environment Agency (EEA) Biodiversity data centre (BDC)";
46
    public static final String DOC_URL = "http://semantic.eea.europa.eu/documentation";
47
    public static final String COPYRIGHT_URL = "http://www.eea.europa.eu/legal/eea-data-policy";
48

    
49
    private static final String SPECIES_RDF_FILE_URL = "http://localhost/download/species.rdf.gz"; // http://eunis.eea.europa.eu/rdf/species.rdf.gz
50
    private static final String TAXONOMY_RDF_FILE_URL = "http://localhost/download/taxonomy.rdf.gz"; // http://eunis.eea.europa.eu/rdf/taxonomy.rdf.gz
51
    private static final String LEGALREFS_RDF_FILE_URL = "http://localhost/download/legalrefs.rdf.gz"; // http://eunis.eea.europa.eu/rdf/legalrefs.rdf.gz
52
    private static final String REFERENCES_RDF_FILE_URL = "http://localhost/download/references.rdf.gz"; // http://eunis.eea.europa.eu/rdf/references.rdf.gz
53

    
54
    /**
55
     * check for updates once a day
56
     */
57
    private static final int CHECK_UPDATE_MINUTES = 1; //60 * 24;
58

    
59
    public static final EnumSet<SearchMode> SEARCH_MODES = EnumSet.of(
60
            SearchMode.scientificNameExact,
61
            SearchMode.scientificNameLike,
62
            SearchMode.vernacularNameExact,
63
            SearchMode.vernacularNameLike,
64
            SearchMode.findByIdentifier);
65

    
66
    public static enum RdfSchema {
67

    
68
        /*
69
         *     xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
70
    xmlns:geo="http://www.w3.org/2003/01/geo/wgs84_pos#"
71
    xmlns:rdfs="http://www.w3.org/2000/01/rdf-schema#"
72
    xmlns:dcterms="http://purl.org/dc/terms/"
73
    xmlns:dc="http://purl.org/dc/elements/1.1/"
74
    xmlns:dwc="http://rs.tdwg.org/dwc/terms/"
75
    xmlns:owl="http://www.w3.org/2002/07/owl#"
76
    xmlns="http://eunis.eea.europa.eu/rdf/species-schema.rdf#"
77
    xmlns:sioc="http://rdfs.org/sioc/ns#"
78
    xmlns:skos="http://www.w3.org/2004/02/skos/core#"
79
    xmlns:bibo="http://purl.org/ontology/bibo/"
80
    xmlns:cc="http://creativecommons.org/ns#"
81
    xmlns:foaf="http://xmlns.com/foaf/0.1/"
82
         */
83
        EUNIS_SPECIES("es","http://eunis.eea.europa.eu/rdf/species-schema.rdf#"),
84
        EUNIS_TAXONOMY("et", "http://eunis.eea.europa.eu/rdf/taxonomies-schema.rdf#"),
85
        DWC("dwc", "http://rs.tdwg.org/dwc/terms/"),
86
        RDF("rdf", "http://www.w3.org/1999/02/22-rdf-syntax-ns#"),
87
        RDFS("rdfs", "http://www.w3.org/2000/01/rdf-schema#"),
88
        SKOS_CORE("scos_core", "http://www.w3.org/2004/02/skos/core#"),
89
        DC("dc", "http://purl.org/dc/terms/source"),
90
        DCTERMS("dcterms", "http://purl.org/dc/terms/");
91

    
92
        private String schemaUri;
93
        private String abbreviation;
94
        RdfSchema(String abbreviation, String schemaUri) {
95
            this.abbreviation = abbreviation;
96
            this.schemaUri = schemaUri;
97
        }
98

    
99
        public String schemaUri() {
100

    
101
            return schemaUri;
102
        }
103

    
104
        public String abbreviation() {
105

    
106
            return abbreviation;
107
        }
108

    
109
        public String property(String name) {
110
            return schemaUri + name;
111
        }
112

    
113
    }
114

    
115
    public enum SubCheckListId {
116

    
117
        eunis, natura_2000;
118
    }
119

    
120
    private enum RankLevel{
121

    
122
        Kingdom, Phylum, Clazz, Order, Family, Genus;
123
    }
124

    
125
    public EEA_BDC_Client() {
126

    
127
        super();
128
    }
129

    
130
    public EEA_BDC_Client(String checklistInfoJson) throws DRFChecklistException {
131

    
132
        super(checklistInfoJson);
133
    }
134

    
135
    /**
136
     * {@inheritDoc}
137
     */
138
    @Override
139
    public boolean isStatelessClient() {
140
        return true;
141
    }
142

    
143
    /**
144
     * {@inheritDoc}
145
     */
146
    @Override
147
    public String getTestUrl() {
148
        return SPECIES_RDF_FILE_URL;
149
    }
150

    
151
    /**
152
     * {@inheritDoc}
153
     */
154
    @Override
155
    public int pollIntervalMinutes() {
156
        return CHECK_UPDATE_MINUTES;
157
    }
158

    
159
    /**
160
     * {@inheritDoc}
161
     */
162
    @Override
163
    public String[] updatableResources() {
164
        return new String[] {SPECIES_RDF_FILE_URL, TAXONOMY_RDF_FILE_URL, LEGALREFS_RDF_FILE_URL, REFERENCES_RDF_FILE_URL};
165
    }
166

    
167
    @Override
168
    public void initQueryClient() {
169

    
170
        Neo4jStore neo4jStore = Neo4jStoreManager.provideStoreFor(this);
171
        queryClient = new TinkerPopClient(neo4jStore);
172
    }
173

    
174
    @Override
175
    public ServiceProviderInfo buildServiceProviderInfo() {
176

    
177
        ServiceProviderInfo checklistInfo = new ServiceProviderInfo(ID, LABEL, DOC_URL, COPYRIGHT_URL, getSearchModes());
178
        checklistInfo.addSubChecklist(new ServiceProviderInfo(SubCheckListId.eunis.name(), "EUNIS",
179
                "http://www.eea.europa.eu/themes/biodiversity/eunis/eunis-db#tab-metadata",
180
                "http://www.eea.europa.eu/legal/copyright", SEARCH_MODES));
181
        return checklistInfo;
182
    }
183

    
184

    
185
    /**
186
     * @param queryString
187
     * @throws DRFChecklistException
188
     */
189
    private void addPrexfixes(StringBuilder queryString) throws DRFChecklistException {
190

    
191
        for(RdfSchema schema : RdfSchema.values()) {
192
            queryString.append(String.format("PREFIX %s: <%s>\n", schema.abbreviation(), schema.schemaUri()));
193
        }
194
    }
195

    
196
    /**
197
     * @param checklistInfo
198
     * @return
199
     * @throws DRFChecklistException
200
     */
201
    private StringBuilder prepareQueryString() throws DRFChecklistException {
202

    
203
        StringBuilder queryString = new StringBuilder();
204
        addPrexfixes(queryString);
205
        return queryString;
206
    }
207

    
208
    private Taxon createTaxon(Vertex v) {
209

    
210
        Taxon taxon = new Taxon();
211

    
212
        TaxonName taxonName = createTaxonName(v);
213

    
214
        // Taxon
215
        taxon.setTaxonName(taxonName);
216
        taxon.setIdentifier(v.getId().toString());
217
        taxon.setAccordingTo(queryClient.relatedVertexValue(v, RdfSchema.DWC, "nameAccordingToID"));
218
        URI typeUri = queryClient.vertexURI(v, RdfSchema.RDF, "type");
219
        taxon.setTaxonomicStatus(typeUri.getFragment());
220

    
221
        createSources(v, taxon);
222

    
223
        // classification
224
        Classification c = null;
225
        Vertex parentV= null;
226
        try {
227
            parentV = queryClient.relatedVertex(v, RdfSchema.EUNIS_SPECIES, "taxonomy");
228
        } catch (Exception e) {
229
            logger.error("No taxonomy information for " + v.toString());
230
        }
231

    
232
        while (parentV != null) {
233
            logger.debug("parent taxon: " + parentV.toString());
234
            String level = queryClient.relatedVertexValue(parentV, RdfSchema.EUNIS_TAXONOMY, "level");
235
            String parentTaxonName = queryClient.relatedVertexValue(parentV, RdfSchema.EUNIS_TAXONOMY, "name");
236

    
237
            RankLevel rankLevel = null;
238
            try {
239
                rankLevel = RankLevel.valueOf(level);
240
            } catch (Exception e) {
241
                // IGNORE
242
            }
243
            if(rankLevel != null) {
244
                if(c == null) {
245
                 c = new Classification();
246
                }
247
                switch(rankLevel) {
248
                case Clazz:
249
                    c.setClazz(parentTaxonName);
250
                    break;
251
                case Family:
252
                    c.setFamily(parentTaxonName);
253
                    break;
254
                case Genus:
255
                    c.setGenus(parentTaxonName);
256
                    break;
257
                case Kingdom:
258
                    c.setKingdom(parentTaxonName);
259
                    break;
260
                case Order:
261
                    c.setOrder(parentTaxonName);
262
                    break;
263
                case Phylum:
264
                    c.setPhylum(parentTaxonName);
265
                    break;
266
                default:
267
                    break;
268
                }
269
            }
270
            Vertex lastParentV = parentV;
271
            parentV = queryClient.relatedVertex(parentV, RdfSchema.EUNIS_TAXONOMY, "parent");
272
            if(lastParentV.equals(parentV)) {
273
                // avoid endless looping when data is not correct
274
                break;
275
            }
276
        }
277
        if(c != null) {
278
            taxon.setClassification(c);
279
        }
280
        return taxon;
281
    }
282

    
283
    /**
284
     * @param model
285
     * @param taxonR
286
     * @param taxonBase
287
     */
288
    private void createSources(Vertex v, TaxonBase taxonBase) {
289

    
290
        // Sources are source references, re there others like data bases?
291

    
292
        GremlinPipeline<Graph, Vertex> taxonPipe = new GremlinPipeline<Graph, Vertex>(v);
293

    
294
        try {
295
            List<Vertex> titleVs = taxonPipe
296
                    .outE(RdfSchema.EUNIS_SPECIES.property("hasLegalReference")).inV()
297
                    .outE(RdfSchema.DCTERMS.property("source")).inV().dedup()
298
                    .outE(RdfSchema.DCTERMS.property("title")).inV()
299
                    .toList();
300
            for(Vertex tv : titleVs) {
301
                Source source = new Source();
302
                logger.error(tv.toString());
303
                source.setName(tv.getProperty(GraphSail.VALUE).toString());
304
                taxonBase.getSources().add(source);
305
            }
306
        } catch (FastNoSuchElementException e) {
307
            logger.debug("No sources found");
308
        }
309
    }
310

    
311
    /**
312
     * @param taxonR
313
     * @return
314
     */
315
    private TaxonName createTaxonName(Vertex v) {
316

    
317
        TaxonName taxonName = new TaxonName();
318
        // TaxonName
319
        taxonName.setFullName(queryClient.relatedVertexValue(v, RdfSchema.RDFS, "label"));
320
        taxonName.setCanonicalName(queryClient.relatedVertexValue(v, RdfSchema.EUNIS_SPECIES, "binomialName"));
321
        taxonName.setRank(queryClient.relatedVertexValue(v, RdfSchema.EUNIS_SPECIES, "taxonomicRank"));
322
        return taxonName;
323
    }
324

    
325

    
326
    private void createSynonyms(Vertex taxonV, Response tnrResponse) {
327

    
328

    
329
        GremlinPipeline<Graph, Vertex> taxonPipe = new GremlinPipeline<Graph, Vertex>(taxonV);
330

    
331
        try {
332
            List<Vertex> synonymVs = taxonPipe
333
                    .inE(RdfSchema.EUNIS_SPECIES.property("eunisPrimaryName")).outV().dedup()
334
                    .toList();
335
            for(Vertex synonymV : synonymVs) {
336
                String typeUri = queryClient.relatedVertexValue(synonymV, RdfSchema.RDF, "type");
337
                String status = null;
338
                try {
339
                    status = URI.create(typeUri).getFragment();
340
                } catch (Exception e) {
341

    
342
                }
343

    
344
                if (status != null && status.equals("SpeciesSynonym")) {
345

    
346
                    Synonym synonym = new Synonym();
347

    
348
                    TaxonName taxonName = createTaxonName(synonymV);
349
                    synonym.setTaxonomicStatus(status);
350
                    synonym.setTaxonName(taxonName);
351
                    synonym.setAccordingTo(queryClient.relatedVertexValue(synonymV, RdfSchema.DWC, "nameAccordingToID"));
352

    
353
                    createSources(synonymV, synonym);
354

    
355
                    tnrResponse.getSynonym().add(synonym);
356
                }
357
            }
358
        } catch (FastNoSuchElementException e) {
359
            logger.debug("No sources found");
360
        }
361

    
362
    }
363

    
364
    @Override
365
    public void resolveScientificNamesExact(TnrMsg tnrMsg) throws DRFChecklistException {
366

    
367
        for (ServiceProviderInfo checklistInfo : getServiceProviderInfo().getSubChecklists()) {
368

    
369
            // FIXME query specific subchecklist
370

    
371
            // selecting one request as representative, only
372
            // the search mode and addSynonmy flag are important
373
            // for the further usage of the request object
374
            Query query = singleQueryFrom(tnrMsg);
375

    
376
            String queryString = query.getRequest().getQueryString();
377
            logger.debug("original queryString: "+ queryString);
378
            queryString = QueryParser.escape(queryString);
379
            queryString = queryString.replace(" ", "\\ ");
380
            if(query.getRequest().getSearchMode().equals(SearchMode.scientificNameLike.name())) {
381
                queryString += "*";
382
            }
383
            logger.debug("prepared queryString: "+ queryString);
384

    
385
            GremlinPipeline<Graph, Vertex> pipe = null;
386

    
387
            Profiler profiler = Profiler.newCpuProfiler(false);
388

    
389
            logger.debug("Neo4jINDEX");
390

    
391
            ArrayList<Vertex> hitVs = queryClient.vertexIndexQuery("value:" + queryString);
392
            pipe = new GremlinPipeline<Graph, Vertex>(hitVs);
393

    
394
            List<Vertex> vertices = new ArrayList<Vertex>();
395
            pipe.in(RdfSchema.EUNIS_SPECIES.property("binomialName")).fill(vertices);
396

    
397
            updateQueriesWithResponse(vertices, null, null, checklistInfo, query);
398
            profiler.end(System.err);
399
        }
400
    }
401

    
402
    @Override
403
    public void resolveScientificNamesLike(TnrMsg tnrMsg) throws DRFChecklistException {
404
        // delegate to resolveScientificNamesExact,
405
        resolveScientificNamesExact(tnrMsg);
406

    
407
    }
408

    
409
    @Override
410
    public void resolveVernacularNamesExact(TnrMsg tnrMsg) throws DRFChecklistException {
411
        List<Query> queryList = tnrMsg.getQuery();
412

    
413
        for (ServiceProviderInfo checklistInfo : getServiceProviderInfo().getSubChecklists()) {
414

    
415
            // FIXME query specific subchecklist
416

    
417
            // selecting one request as representative, only
418
            // the search mode and addSynonmy flag are important
419
            // for the further usage of the request object
420
            Query query = singleQueryFrom(tnrMsg);
421

    
422
            String queryString = query.getRequest().getQueryString();
423
            logger.debug("original queryString: "+ queryString);
424
            queryString = QueryParser.escape(queryString);
425
            queryString = queryString.replace(" ", "\\ ");
426
            if(query.getRequest().getSearchMode().equals(SearchMode.vernacularNameLike.name())) {
427
                queryString = "*" + queryString + "*";
428
            }
429

    
430
            logger.debug("prepared queryString: "+ queryString);
431

    
432
            GremlinPipeline<Graph, Vertex> pipe = null;
433

    
434
            Profiler profiler = Profiler.newCpuProfiler(false);
435

    
436
            // by using the Neo4j index directly it is possible to
437
            // take full advantage of the underlying Lucene search engine
438
            ArrayList<Vertex> hitVs = queryClient.vertexIndexQuery("value:" + queryString);
439

    
440
//            List<String> matchingNames = new ArrayList<String>(hitVs.size());
441
//            for(Vertex v : hitVs) {
442
//                String matchValue = v.getProperty(GraphSail.VALUE).toString();
443
//                matchingNames.add(matchValue);
444
//                logger.debug("matchingName  " + matchValue);
445
//            }
446

    
447
            List<Vertex> vertices = new ArrayList<Vertex>();
448
            pipe = new GremlinPipeline<Graph, Vertex>(hitVs);
449
            Table table = new Table();
450
            pipe.as("match").in(RdfSchema.DWC.property("vernacularName")).as("taxon").table(table).iterate();
451

    
452
            updateQueriesWithResponse(
453
                    table.getColumn("taxon"), table.getColumn("match"),
454
                    NameType.VERNACULAR_NAME, checklistInfo, query);
455
            profiler.end(System.err);
456
        }
457
    }
458

    
459
    @Override
460
    public void resolveVernacularNamesLike(TnrMsg tnrMsg) throws DRFChecklistException {
461
        resolveVernacularNamesExact(tnrMsg);
462
    }
463

    
464
    @Override
465
    public void findByIdentifier(TnrMsg tnrMsg) throws DRFChecklistException {
466

    
467
        for (ServiceProviderInfo checklistInfo : getServiceProviderInfo().getSubChecklists()) {
468

    
469
            // FIXME query specific subchecklist
470
            Query query = singleQueryFrom(tnrMsg);
471
            String queryString = query.getRequest().getQueryString();
472

    
473
            // by using the Neo4j index directly it is possible to
474
            // take full advantage of the underlying Lucene search engine
475
            queryString = QueryParser.escape(queryString);
476
            ArrayList<Vertex> hitVs = queryClient.vertexIndexQuery("value:" + queryString);
477
            if(hitVs.size() > 0) {
478
                Response response = tnrResponseFromResource(hitVs.get(0), query.getRequest(), null, null);
479
                query.getResponse().add(response);
480
            } else if(hitVs.size() > 1) {
481
                throw new DRFChecklistException("More than one node with the id '" + queryString + "' found");
482
            }
483
        }
484
    }
485

    
486
    private void updateQueriesWithResponse(List<Vertex> taxonNodes, List<Vertex> matchNodes, NameType matchType, ServiceProviderInfo ci, Query query){
487

    
488
        if (taxonNodes == null) {
489
            return;
490
        }
491

    
492
        logger.debug("matching taxon nodes:");
493
        int i = -1;
494
        for (Vertex v : taxonNodes) {
495
            i++;
496
            logger.debug("  " + v.toString());
497
            printPropertyKeys(v, System.err);
498
            if(v.getProperty("kind").equals("url")) {
499
                logger.error("vertex of type 'url' expected, but was " + v.getProperty("type").equals("url"));
500
                continue;
501
            }
502
            Vertex matchNode = null;
503
            if(matchNodes != null) {
504
                matchNode = matchNodes.get(i);
505
            }
506
            Response tnrResponse = tnrResponseFromResource(v, query.getRequest(), matchNode, matchType);
507
            if(tnrResponse != null) {
508
                query.getResponse().add(tnrResponse);
509
            }
510
        }
511
    }
512

    
513
    /**
514
     * @param model
515
     * @param taxonR
516
     * @param request
517
     * @param matchType
518
     * @param matchNode
519
     * @return
520
     */
521
    private Response tnrResponseFromResource(Vertex taxonV, Request request, Vertex matchNode, NameType matchType) {
522

    
523
        Response tnrResponse = TnrMsgUtils.tnrResponseFor(getServiceProviderInfo());
524

    
525
        GremlinPipeline<Graph, Vertex> pipe = new GremlinPipeline<Graph, Vertex>(taxonV);
526

    
527
        String validName = queryClient.relatedVertexValue(taxonV, RdfSchema.EUNIS_SPECIES, "validName");
528

    
529
        boolean isAccepted = validName != null && validName.equals("true");
530

    
531
        logger.debug("processing " + (isAccepted ? "accepted taxon" : "synonym or other")  + " " + taxonV.getId());
532

    
533
        //
534
        if(matchNode != null) {
535
            String matchingName = matchNode.getProperty(GraphSail.VALUE).toString();
536
            tnrResponse.setMatchingNameString(matchingName);
537
            tnrResponse.setMatchingNameType(matchType);
538
        }
539

    
540
        // case when accepted name
541
        if(isAccepted) {
542
            Taxon taxon = createTaxon(taxonV);
543
            tnrResponse.setTaxon(taxon);
544
            if(matchNode == null) {
545
                tnrResponse.setMatchingNameType(NameType.TAXON);
546
                String matchingName = taxon.getTaxonName().getCanonicalName();
547
                tnrResponse.setMatchingNameString(matchingName);
548
            }
549

    
550
        }
551
        else {
552
            // case when synonym
553
            Vertex synonymV = taxonV;
554
            taxonV = null;
555
            try {
556
                taxonV = queryClient.relatedVertex(synonymV, RdfSchema.EUNIS_SPECIES, "eunisPrimaryName");
557
            } catch(Exception e) {
558
                logger.error("No accepted taxon found for " + synonymV.toString() + " (" + synonymV.getProperty(GraphSail.VALUE) + ")");
559
            }
560

    
561
            if(taxonV != null) {
562
                Taxon taxon = createTaxon(taxonV);
563
                tnrResponse.setTaxon(taxon);
564
            } else {
565
            }
566
            if(matchNode == null) {
567
                tnrResponse.setMatchingNameType(NameType.SYNONYM);
568
                String matchingName = queryClient.relatedVertexValue(synonymV, RdfSchema.EUNIS_SPECIES, "binomialName");
569
                tnrResponse.setMatchingNameString(matchingName);
570
            }
571
        }
572

    
573
        if(request.isAddSynonymy()) {
574
            createSynonyms(taxonV, tnrResponse);
575
        }
576

    
577
        logger.debug("processing " + (isAccepted ? "accepted taxon" : "synonym or other")  + " " + taxonV.getId() + " DONE");
578
        return tnrResponse;
579
    }
580

    
581
    /**
582
     * @param vertex
583
     */
584
    private void printEdges(Neo4j2Vertex vertex) {
585
        Iterable<Relationship> rels = vertex.getRawVertex().getRelationships();
586
        Iterator<Relationship> iterator = rels.iterator();
587
        if(iterator.hasNext()) {
588
            Relationship rel = iterator.next();
589
            System.err.println(rel.toString() + ": " + rel.getStartNode().toString() + "-[" +  rel.getType() + "]-" + rel.getEndNode().toString());
590
        }
591
    }
592

    
593
    private void printPropertyKeys(Vertex v, PrintStream ps) {
594
        StringBuilder out = new StringBuilder();
595
        out.append(v.toString());
596
        for(String key : v.getPropertyKeys()) {
597
            out.append(key).append(": ").append(v.getProperty(key)).append(" ");
598
        }
599
        ps.println(out.toString());
600
    }
601

    
602
    @Override
603
    public EnumSet<SearchMode> getSearchModes() {
604
        return SEARCH_MODES;
605
    }
606

    
607
    @Override
608
    public boolean isSupportedIdentifier(String value) {
609
        return IdentifierUtils.checkURI(value);
610
    }
611

    
612

    
613
}
(5-5/13)