source: AnnotationManagerN4J/src/main/java/de/mpiwg/itgroup/annotations/neo4j/AnnotationStore.java @ 76:4e2dc67997a0

Last change on this file since 76:4e2dc67997a0 was 76:4e2dc67997a0, checked in by casties, 10 years ago

save text quote from Annotator.

File size: 39.8 KB
Line 
1/**
2 *
3 */
4package de.mpiwg.itgroup.annotations.neo4j;
5
6/*
7 * #%L
8 * AnnotationManager
9 * %%
10 * Copyright (C) 2012 - 2014 MPIWG Berlin
11 * %%
12 * This program is free software: you can redistribute it and/or modify
13 * it under the terms of the GNU Lesser General Public License as
14 * published by the Free Software Foundation, either version 3 of the
15 * License, or (at your option) any later version.
16 *
17 * This program is distributed in the hope that it will be useful,
18 * but WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
20 * GNU General Lesser Public License for more details.
21 *
22 * You should have received a copy of the GNU General Lesser Public
23 * License along with this program.  If not, see
24 * <http://www.gnu.org/licenses/lgpl-3.0.html>.
25 * #L%
26 * Author: Robert Casties (casties@mpiwg-berlin.mpg.de)
27 */
28
29import java.util.ArrayList;
30import java.util.Arrays;
31import java.util.Calendar;
32import java.util.HashSet;
33import java.util.List;
34import java.util.Set;
35import java.util.logging.Logger;
36
37import org.neo4j.graphdb.Direction;
38import org.neo4j.graphdb.GraphDatabaseService;
39import org.neo4j.graphdb.Node;
40import org.neo4j.graphdb.Relationship;
41import org.neo4j.graphdb.RelationshipType;
42import org.neo4j.graphdb.Transaction;
43import org.neo4j.graphdb.index.Index;
44import org.neo4j.graphdb.index.IndexHits;
45
46import de.mpiwg.itgroup.annotations.Actor;
47import de.mpiwg.itgroup.annotations.Annotation;
48import de.mpiwg.itgroup.annotations.Annotation.FragmentTypes;
49import de.mpiwg.itgroup.annotations.Group;
50import de.mpiwg.itgroup.annotations.Person;
51import de.mpiwg.itgroup.annotations.Resource;
52import de.mpiwg.itgroup.annotations.Tag;
53import de.mpiwg.itgroup.annotations.Target;
54import de.mpiwg.itgroup.annotations.Uri;
55
56/**
57 * Neo4J based Annotation store.
58 *
59 * @author casties
60 *
61 */
62public class AnnotationStore {
63
64    protected static Logger logger = Logger.getLogger("de.mpiwg.itgroup.annotations.neo4j.AnnotationStore");
65
66    protected GraphDatabaseService graphDb;
67
68    public static enum NodeTypes {
69        ANNOTATION, PERSON, TARGET, GROUP, TAG, RESOURCE
70    }
71
72    // types of nodes that should not be automatically deleted.
73    public Set<String> permanentNodeTypes = new HashSet<String>(Arrays.asList("PERSON", "GROUP", "TAG"));
74
75    protected List<Index<Node>> nodeIndexes;
76
77    public static enum RelationTypes implements RelationshipType {
78        ANNOTATES, CREATED, PERMITS_ADMIN, PERMITS_DELETE, PERMITS_UPDATE, PERMITS_READ, MEMBER_OF, HAS_TAG, PART_OF
79    }
80
81    public static String ANNOTATION_URI_PREFIX = "";
82
83    public AnnotationStore(GraphDatabaseService graphDb) {
84        super();
85        this.graphDb = graphDb;
86        nodeIndexes = new ArrayList<Index<Node>>(5);
87        // List.set(enum.ordinal(), val) seems not to work.
88        try (Transaction tx = graphDb.beginTx()) {
89            nodeIndexes.add(NodeTypes.ANNOTATION.ordinal(), graphDb.index().forNodes("annotations"));
90            nodeIndexes.add(NodeTypes.PERSON.ordinal(), graphDb.index().forNodes("persons"));
91            nodeIndexes.add(NodeTypes.TARGET.ordinal(), graphDb.index().forNodes("targets"));
92            nodeIndexes.add(NodeTypes.GROUP.ordinal(), graphDb.index().forNodes("groups"));
93            nodeIndexes.add(NodeTypes.TAG.ordinal(), graphDb.index().forNodes("tags"));
94            nodeIndexes.add(NodeTypes.RESOURCE.ordinal(), graphDb.index().forNodes("resources"));
95            tx.success();
96        }
97    }
98
99    protected Index<Node> getNodeIndex(NodeTypes type) {
100        return nodeIndexes.get(type.ordinal());
101    }
102
103    /**
104     * @param userUri
105     * @return
106     */
107    public Node getPersonNodeByUri(String userUri) {
108        return getNodeFromIndex("uri", userUri, NodeTypes.PERSON);
109    }
110
111    /**
112     * @param tagUri
113     * @return
114     */
115    public Node getTagNodeByUri(String tagUri) {
116        return getNodeFromIndex("uri", tagUri, NodeTypes.TAG);
117    }
118
119    /**
120     * @param resourceUri
121     * @return
122     */
123    public Node getResourceNodeByUri(String resourceUri) {
124        return getNodeFromIndex("uri", resourceUri, NodeTypes.RESOURCE);
125    }
126
127    /**
128     * @param targetUri
129     * @return
130     */
131    public Node getTargetNodeByUri(String targetUri) {
132        return getNodeFromIndex("uri", targetUri, NodeTypes.RESOURCE);
133    }
134
135    /**
136     * Returns the Node with the given key and value. Key has to be indexed.
137     *
138     * @param key
139     * @param value
140     * @param type
141     * @return
142     */
143    public Node getNodeFromIndex(String key, String value, NodeTypes type) {
144        if (key == null || value == null)
145            return null;
146        Node node = null;
147        try (Transaction tx = graphDb.beginTx()) {
148            node = getNodeIndex(type).get(key, value).getSingle();
149            tx.success();
150        }
151        return node;
152    }
153
154    /**
155     * Returns list of Actors of given type (Group or Person). Key has to be
156     * indexed.
157     *
158     * @param key
159     * @param query
160     * @param type
161     * @return
162     */
163    @SuppressWarnings("unchecked")
164    protected <T extends Actor> List<T> getActors(String key, String query, NodeTypes type) {
165        ArrayList<T> actors = new ArrayList<T>();
166        Index<Node> idx = getNodeIndex(type);
167        if (key == null) {
168            key = "uri";
169            query = "*";
170        }
171        try (Transaction tx = graphDb.beginTx()) {
172            IndexHits<Node> actorNodes = idx.query(key, query);
173            for (Node actorNode : actorNodes) {
174                Actor actor = createActorFromNode(actorNode);
175                actors.add((T) actor);
176            }
177            tx.success();           
178        }
179        return actors;
180    }
181
182    /**
183     * Returns list of groups. Key has to be indexed.
184     *
185     * @param key
186     * @param query
187     * @return
188     */
189    public List<Group> getGroups(String key, String query) {
190        List<Group> groups = getActors(key, query, NodeTypes.GROUP);
191        return groups;
192    }
193
194    /**
195     * Returns list of Persons. Key has to be indexed.
196     *
197     * @param key
198     * @param query
199     * @return
200     */
201    public List<Person> getPersons(String key, String query) {
202        List<Person> persons = getActors(key, query, NodeTypes.PERSON);
203        return persons;
204    }
205
206    /**
207     * Returns list of uri-like objects of given type (Target or Resource). Key
208     * has to be indexed.
209     *
210     * @param key
211     * @param query
212     * @param type
213     * @return
214     */
215    @SuppressWarnings("unchecked")
216    protected <T extends Uri> List<T> getUris(String key, String query, NodeTypes type) {
217        ArrayList<T> uris = new ArrayList<T>();
218        Index<Node> idx = getNodeIndex(type);
219        if (key == null) {
220            key = "uri";
221            query = "*";
222        }
223        try (Transaction tx = graphDb.beginTx()) {
224            IndexHits<Node> actorNodes = idx.query(key, query);
225            for (Node actorNode : actorNodes) {
226                Uri uri = createUriFromNode(actorNode);
227                uris.add((T) uri);
228            }
229            tx.success();
230        }
231        return uris;
232    }
233
234    /**
235     * Returns list of Targets. Key has to be indexed.
236     *
237     * @param key
238     * @param query
239     * @return
240     */
241    public List<Target> getTargets(String key, String query) {
242        List<Target> targets = getUris(key, query, NodeTypes.TARGET);
243        return targets;
244    }
245
246    /**
247     * Returns list of Resources. Key has to be indexed.
248     *
249     * @param key
250     * @param query
251     * @return
252     */
253    public List<Resource> getResources(String key, String query) {
254        List<Resource> targets = getUris(key, query, NodeTypes.RESOURCE);
255        return targets;
256    }
257
258    /**
259     * Returns List of Annotations. Key has to be indexed.
260     *
261     * @param key
262     * @param query
263     * @return
264     */
265    public List<Annotation> getAnnotations(String key, String query) {
266        ArrayList<Annotation> annotations = new ArrayList<Annotation>();
267        Index<Node> idx = getNodeIndex(NodeTypes.ANNOTATION);
268        if (key == null) {
269            key = "id";
270            query = "*";
271        }
272        try (Transaction tx = graphDb.beginTx()) {
273            IndexHits<Node> annotNodes = idx.query(key, query);
274            for (Node annotNode : annotNodes) {
275                Annotation annotation = createAnnotationFromNode(annotNode);
276                annotations.add(annotation);
277            }
278            tx.success();
279        }
280        return annotations;
281    }
282
283    /**
284     * Returns List of Tags. Key has to be indexed.
285     *
286     * @param key
287     * @param query
288     * @return
289     */
290    public List<Tag> getTags(String key, String query) {
291        ArrayList<Tag> tags = new ArrayList<Tag>();
292        Index<Node> idx = getNodeIndex(NodeTypes.TAG);
293        if (key == null) {
294            key = "uri";
295            query = "*";
296        }
297        try (Transaction tx = graphDb.beginTx()) {
298            IndexHits<Node> groupNodes = idx.query(key, query);
299            for (Node groupNode : groupNodes) {
300                Tag tag = createTagFromNode(groupNode);
301                tags.add(tag);
302            }
303            tx.success();
304        }
305        return tags;
306    }
307
308    /**
309     * Returns List of Groups the person is member of.
310     *
311     * @param person
312     * @return
313     */
314    public List<Group> getGroupsForPersonNode(Node person) {
315        ArrayList<Group> groups = new ArrayList<Group>();
316        Iterable<Relationship> rels = person.getRelationships(RelationTypes.MEMBER_OF);
317        for (Relationship rel : rels) {
318            Node groupNode = rel.getEndNode();
319            Actor group = createActorFromNode(groupNode);
320            // make sure we're getting a group
321            if (!(group instanceof Group)) {
322                logger.severe("target of MEMBER_OF is not GROUP! rel=" + rel);
323                continue;
324            }
325            groups.add((Group) group);
326        }
327        return groups;
328    }
329
330    /**
331     * Returns if person with uri is in Group group.
332     *
333     * @param person
334     * @param group
335     * @return
336     */
337    public boolean isPersonInGroup(Person person, Group group) {
338        Node pn = getPersonNodeByUri(person.getUriString());
339        if (pn == null)
340            return false;
341        // optimized version of getGroupsForPersonNode
342        try (Transaction tx = graphDb.beginTx()) {
343            Iterable<Relationship> rels = pn.getRelationships(RelationTypes.MEMBER_OF);
344            for (Relationship rel : rels) {
345                Node gn = rel.getEndNode();
346                if (gn.getProperty("uri", "").equals(group.getUriString()) 
347                        || gn.getProperty("id", "").equals(group.getId())) {
348                    tx.success();
349                    return true;
350                }
351            }
352            tx.success();
353        }
354        return false;
355    }
356
357    /**
358     * Returns the members of the group.
359     *
360     * @param group
361     * @return
362     */
363    public List<Person> getMembersOfGroup(Group group) {
364        ArrayList<Person> members = new ArrayList<Person>();
365        Node gn = getActorNode(group);
366        Iterable<Relationship> rels = gn.getRelationships(RelationTypes.MEMBER_OF);
367        for (Relationship rel : rels) {
368            Node memberNode = rel.getStartNode();
369            Actor member = createActorFromNode(memberNode);
370            // make sure we're getting a group
371            if (!(member instanceof Person)) {
372                logger.severe("source of MEMBER_OF is not PERSON! rel=" + rel);
373                continue;
374            }
375            members.add((Person) member);
376        }
377        return members;
378    }
379
380    /**
381     * Add Person newMember to Group group.
382     *
383     * @param group
384     * @param member
385     */
386    public Person addGroupMember(Group group, Person member) {
387        Node gn = getActorNode(group);
388        Node pn = getActorNode(member);
389        Person addedMember = null;
390        if (gn != null && pn != null) {
391            getOrCreateRelation(pn, RelationTypes.MEMBER_OF, gn);
392            addedMember = member;
393        }
394        return addedMember;
395    }
396
397    /**
398     * Delete Person oldMember from Group group.
399     *
400     * @param group
401     * @param member
402     */
403    public void deleteGroupMember(Group group, Person member) {
404        Node gn = getActorNode(group);
405        Node pn = getActorNode(member);
406        Iterable<Relationship> rels = gn.getRelationships(RelationTypes.MEMBER_OF);
407        for (Relationship rel : rels) {
408            Node mn = rel.getStartNode();
409            if (mn.equals(pn)) {
410                try (Transaction tx = graphDb.beginTx()) {
411                    rel.delete();
412                    tx.success();
413                }
414                // there should be only one
415                break;
416            }
417        }
418    }
419
420    /**
421     * Returns the stored Actor matching the given one.
422     *
423     * @param actor
424     * @return
425     */
426    public Actor getActor(Actor actor) {
427        Node actorNode = getActorNode(actor);
428        Actor storedActor = createActorFromNode(actorNode);
429        return storedActor;
430    }
431
432    /**
433     * Stores an Actor (Person or Group). Creates a new actor Node or update an
434     * existing one.
435     *
436     * @param actor
437     * @return
438     */
439    public Actor storeActor(Actor actor) {
440        Node actorNode = getOrCreateActorNode(actor);
441        try (Transaction tx = graphDb.beginTx()) {
442            // id
443            String id = actor.getId();
444            if (id != null) {
445                actorNode.setProperty("id", id);
446            }
447            // name
448            String name = actor.getName();
449            if (name != null) {
450                actorNode.setProperty("name", name);
451            }
452            // uri
453            String uri = actor.getUri();
454            if (uri != null) {
455                actorNode.setProperty("uri", uri);
456            }
457            tx.success();
458        }
459        Actor storedActor = createActorFromNode(actorNode);
460        return storedActor;
461    }
462
463    /**
464     * Deletes the given Actor.
465     *
466     * @param actor
467     */
468    public void deleteActor(Actor actor) {
469        String uri = actor.getUriString();
470        Index<Node> idx;
471        if (actor.isGroup()) {
472            idx = getNodeIndex(NodeTypes.GROUP);
473        } else {
474            idx = getNodeIndex(NodeTypes.PERSON);
475        }
476        Node actorNode = idx.get("uri", uri).getSingle();
477        if (actorNode != null) {
478            // delete relations
479            try (Transaction tx = graphDb.beginTx()) {
480                for (Relationship rel : actorNode.getRelationships()) {
481                    rel.delete();
482                }
483                if (!actorNode.hasRelationship()) {
484                    // this shouldn't happen
485                    deleteNode(actorNode);
486                } else {
487                    logger.severe("deleteActor: unable to delete: Node still has relations.");
488                }
489                tx.success();
490            }
491        }
492    }
493
494    /**
495     * Returns the Annotation with the given id.
496     *
497     * @param id
498     * @return
499     */
500    public Annotation getAnnotationById(String id) {
501        Annotation annot = null;
502        try (Transaction tx = graphDb.beginTx()) {
503            Node annotNode = getNodeIndex(NodeTypes.ANNOTATION).get("id", id).getSingle();
504            annot = createAnnotationFromNode(annotNode);
505            tx.success();
506        }
507        return annot;
508    }
509
510    /**
511     * Returns an Annotation object from an annotation-Node.
512     *
513     * @param annotNode
514     * @return
515     */
516    public Annotation createAnnotationFromNode(Node annotNode) {
517        Annotation annot = new Annotation();
518        try (Transaction tx = graphDb.beginTx()) {
519            annot.setUri((String) annotNode.getProperty("id", null));
520            annot.setBodyText((String) annotNode.getProperty("bodyText", null));
521            annot.setBodyUri((String) annotNode.getProperty("bodyUri", null));
522            annot.setQuote((String) annotNode.getProperty("quote", null));
523            /*
524             * get annotation target and resource from relation
525             */
526            for (Relationship rel : annotNode.getRelationships(RelationTypes.ANNOTATES)) {
527                Node target = rel.getEndNode();
528                String type = (String) target.getProperty("TYPE");
529                if (type.equals("TARGET")) {
530                    annot.setTarget(new Target((String) target.getProperty("uri", null)));
531                } else if (type.equals("RESOURCE")) {
532                    annot.setResource(new Resource((String) target.getProperty("uri", null)));
533                }
534            }
535            if (annot.getTarget() == null) {
536                logger.warning("annotation " + annotNode + " has no target node!");
537            }
538            // get fragment from attribute
539            annot.setTargetFragment((String) annotNode.getProperty("targetFragment", null));
540            String ft = (String) annotNode.getProperty("fragmentType", null);
541            if (ft != null) {
542                annot.setFragmentType(FragmentTypes.valueOf(ft));
543            }
544            /*
545             * get creator from relation
546             */
547            Relationship creatorRel = getRelation(annotNode, RelationTypes.CREATED, null);
548            if (creatorRel != null) {
549                Node creatorNode = creatorRel.getStartNode();
550                Actor creator = createActorFromNode(creatorNode);
551                annot.setCreator(creator);
552            } else {
553                logger.warning("annotation " + annotNode + " has no creator node!");
554            }
555            /*
556             * get creation date
557             */
558            annot.setCreated((String) annotNode.getProperty("created", null));
559            /*
560             * get permissions
561             */
562            Relationship adminRel = getRelation(annotNode, RelationTypes.PERMITS_ADMIN, null);
563            if (adminRel != null) {
564                Node adminNode = adminRel.getEndNode();
565                Actor admin = createActorFromNode(adminNode);
566                annot.setAdminPermission(admin);
567            }
568            Relationship deleteRel = getRelation(annotNode, RelationTypes.PERMITS_DELETE, null);
569            if (deleteRel != null) {
570                Node deleteNode = deleteRel.getEndNode();
571                Actor delete = createActorFromNode(deleteNode);
572                annot.setDeletePermission(delete);
573            }
574            Relationship updateRel = getRelation(annotNode, RelationTypes.PERMITS_UPDATE, null);
575            if (updateRel != null) {
576                Node updateNode = updateRel.getEndNode();
577                Actor update = createActorFromNode(updateNode);
578                annot.setUpdatePermission(update);
579            }
580            Relationship readRel = getRelation(annotNode, RelationTypes.PERMITS_READ, null);
581            if (readRel != null) {
582                Node readNode = readRel.getEndNode();
583                Actor read = createActorFromNode(readNode);
584                annot.setReadPermission(read);
585            }
586            /*
587             * get tags
588             */
589            Set<String> tags = new HashSet<String>();
590            for (Relationship rel : annotNode.getRelationships(RelationTypes.HAS_TAG)) {
591                String tag = (String) rel.getEndNode().getProperty("name", null);
592                if (tag != null) {
593                    tags.add(tag);
594                }
595            }
596            annot.setTags(tags);
597
598            tx.success();
599        }
600        return annot;
601    }
602
603    /**
604     * Returns an Actor object from a node.
605     *
606     * @param actorNode
607     * @return
608     */
609    protected Actor createActorFromNode(Node actorNode) {
610        if (actorNode == null)
611            return null;
612        try (Transaction tx = graphDb.beginTx()) {
613            String id = (String) actorNode.getProperty("id", null);
614            String uri = (String) actorNode.getProperty("uri", null);
615            String name = (String) actorNode.getProperty("name", null);
616            String type = (String) actorNode.getProperty("TYPE", null);
617            tx.success();
618            if (type != null && type.equals("PERSON")) {
619                return new Person(id, uri, name);
620            } else if (type != null && type.equals("GROUP")) {
621                return new Group(id, uri, name);
622            }
623        }
624        return null;
625    }
626
627    public Tag createTagFromNode(Node tagNode) {
628        if (tagNode == null)
629            return null;
630        String id;
631        String uri;
632        String name;
633        try (Transaction tx = graphDb.beginTx()) {
634            name = (String) tagNode.getProperty("name", null);
635            uri = (String) tagNode.getProperty("uri", null);
636            id = (String) tagNode.getProperty("id", null);
637            tx.success();
638        }
639        return new Tag(id, uri, name);
640
641    }
642
643    /**
644     * @param resourceNode
645     * @return
646     */
647    public Resource createResourceFromNode(Node resourceNode) {
648        return (Resource) createUriFromNode(resourceNode);
649    }
650
651    /**
652     * @param targetNode
653     * @return
654     */
655    public Target createTargetFromNode(Node targetNode) {
656        return (Target) createUriFromNode(targetNode);
657    }
658
659    protected Uri createUriFromNode(Node uriNode) {
660        if (uriNode == null)
661            return null;
662        try (Transaction tx = graphDb.beginTx()) {
663            String uri = (String) uriNode.getProperty("uri", null);
664            String type = (String) uriNode.getProperty("TYPE", null);
665            tx.success();
666            if (type != null && type.equals("TARGET")) {
667                return new Target(uri);
668            } else if (type != null && type.equals("RESOURCE")) {
669                return new Resource(uri);
670            }
671        }
672        return null;
673    }
674
675    /**
676     * Store a new annotation in the store or update an existing one. Returns
677     * the stored annotation.
678     *
679     * @param annot
680     * @return
681     */
682    public Annotation storeAnnotation(Annotation annot) {
683        Node annotNode = null;
684        try (Transaction tx = graphDb.beginTx()) {
685            /*
686             * create or get the annotation
687             */
688            String id = annot.getUri();
689            if (id == null) {
690                id = createRessourceURI("annot:");
691            }
692            annotNode = getOrCreateAnnotationNode(id);
693
694            /*
695             * the annotation body
696             */
697            String bodyText = annot.getBodyText();
698            if (bodyText != null) {
699                annotNode.setProperty("bodyText", bodyText);
700            }
701            String bodyUri = annot.getBodyUri();
702            if (bodyUri != null) {
703                annotNode.setProperty("bodyUri", bodyUri);
704            }
705           
706            /*
707             * the annotation quote
708             */
709            String quote = annot.getQuote();
710            if (quote != null) {
711                annotNode.setProperty("quote", quote);
712            }
713               
714            /*
715             * the annotation target
716             */
717            Target target = annot.getTarget();
718            Node targetNode = null;
719            if (target != null) {
720                targetNode = getOrCreateUriNode(target.getUri(), NodeTypes.TARGET);
721                getOrCreateRelation(annotNode, RelationTypes.ANNOTATES, targetNode);
722            }
723
724            /*
725             * The fragment part of the annotation target.
726             */
727            String targetFragment = annot.getTargetFragment();
728            FragmentTypes fragmentType = annot.getFragmentType();
729            if (targetFragment != null) {
730                annotNode.setProperty("targetFragment", targetFragment);
731                annotNode.setProperty("fragmentType", fragmentType.name());
732            }
733
734            /*
735             * the annotation resource
736             */
737            Resource resource = annot.getResource();
738            if (resource != null) {
739                Node resourceNode = getOrCreateUriNode(resource.getUri(), NodeTypes.RESOURCE);
740                getOrCreateRelation(annotNode, RelationTypes.ANNOTATES, resourceNode);
741                getOrCreateRelation(targetNode, RelationTypes.PART_OF, resourceNode);
742            }
743
744            /*
745             * The creator of this annotation.
746             */
747            Actor creator = annot.getCreator();
748            if (creator != null) {
749                Node creatorNode = getOrCreateActorNode(creator);
750                getOrCreateRelation(creatorNode, RelationTypes.CREATED, annotNode);
751            }
752
753            /*
754             * The creation date of this annotation.
755             */
756            String created = annot.getCreated();
757            if (created != null) {
758                annotNode.setProperty("created", created);
759            }
760
761            /*
762             * Permissions for this annotation.
763             */
764            setPermissionRelation(annotNode, RelationTypes.PERMITS_ADMIN, annot.getAdminPermission());
765            setPermissionRelation(annotNode, RelationTypes.PERMITS_DELETE, annot.getDeletePermission());
766            setPermissionRelation(annotNode, RelationTypes.PERMITS_UPDATE, annot.getUpdatePermission());
767            setPermissionRelation(annotNode, RelationTypes.PERMITS_READ, annot.getReadPermission());
768
769            /*
770             * Tags on this annotation.
771             */
772            Set<String> newTags = annot.getTags();
773            // we ignore existing tags if tags == null
774            if (newTags != null) {
775                List<Relationship> oldHasTags = new ArrayList<Relationship>();
776                for (Relationship rel : annotNode.getRelationships(RelationTypes.HAS_TAG)) {
777                    oldHasTags.add(rel);
778                }
779                // adjust to new tags
780                if (newTags.isEmpty()) {
781                    // remove old tags
782                    if (!oldHasTags.isEmpty()) {
783                        for (Relationship rel : oldHasTags) {
784                            rel.delete();
785                            // TODO: should we delete orphan nodes too?
786                        }
787                    }
788                } else {
789                    if (!oldHasTags.isEmpty()) {
790                        // adjust old tags
791                        for (Relationship rel : oldHasTags) {
792                            String oldTag = (String) rel.getEndNode().getProperty("name", null);
793                            if (newTags.contains(oldTag)) {
794                                // tag exists
795                                newTags.remove(oldTag);
796                            } else {
797                                // tag exists no longer
798                                rel.delete();
799                                // TODO: should we delete orphan nodes too?
800                            }
801                        }
802                    }
803                    if (!newTags.isEmpty()) {
804                        // still tags to add
805                        for (String tag : newTags) {
806                            // create new tag
807                            Node tagNode = getOrCreateTagNode(new Tag(null, null, tag));
808                            getOrCreateRelation(annotNode, RelationTypes.HAS_TAG, tagNode);
809                        }
810                    }
811
812                }
813            }
814            tx.success();
815        }
816
817        // re-read and return annotation
818        Annotation storedAnnot = createAnnotationFromNode(annotNode);
819        return storedAnnot;
820    }
821
822    /**
823     * Deletes the annotation with the given id.
824     *
825     * @param id
826     */
827    public void deleteAnnotationById(String id) {
828        try (Transaction tx = graphDb.beginTx()) {
829            Node annotNode = getNodeIndex(NodeTypes.ANNOTATION).get("id", id).getSingle();
830            if (annotNode != null) {
831                // delete related objects
832                for (Relationship rel : annotNode.getRelationships()) {
833                    // delete relation and the related node if it has no other
834                    // relations and is not permanent
835                    Node other = rel.getOtherNode(annotNode);
836                    rel.delete();
837                    if (!(other.hasRelationship() || permanentNodeTypes.contains(other.getProperty("TYPE", null)))) {
838                        deleteNode(other);
839                    }
840                }
841                if (!annotNode.hasRelationship()) {
842                    deleteNode(annotNode);
843                } else {
844                    logger.severe("deleteById: unable to delete: Node still has relations.");
845                }
846            }
847            tx.success();
848        }
849    }
850
851    /**
852     * Returns all annotations with the given uri and/or user.
853     *
854     * @param uri
855     * @param userUri
856     * @return
857     */
858    public List<Annotation> searchAnnotationByUriUser(String targetUri, String userUri) {
859        List<Annotation> annotations = new ArrayList<Annotation>();
860        if (targetUri != null) {
861            // there should be only one
862            Node target = getNodeFromIndex("uri", targetUri, NodeTypes.TARGET);
863            if (target != null) {
864                try (Transaction tx = graphDb.beginTx()) {
865                    Iterable<Relationship> relations = target.getRelationships(RelationTypes.ANNOTATES);
866                    for (Relationship relation : relations) {
867                        Node ann = relation.getStartNode();
868                        if (ann.getProperty("TYPE", "").equals("ANNOTATION")) {
869                            Annotation annot = createAnnotationFromNode(ann);
870                            annotations.add(annot);
871                        } else {
872                            logger.severe("ANNOTATES relation does not start with ANNOTATION: " + ann);
873                        }
874                    }
875                    tx.success();
876                }
877            }
878        }
879        if (userUri != null) {
880            // there should be only one
881            Node person = getPersonNodeByUri(userUri);
882            if (person != null) {
883                try (Transaction tx = graphDb.beginTx()) {
884                    Iterable<Relationship> relations = person.getRelationships(RelationTypes.CREATED);
885                    for (Relationship relation : relations) {
886                        Node ann = relation.getEndNode();
887                        if (ann.getProperty("TYPE", "").equals("ANNOTATION")) {
888                            Annotation annot = createAnnotationFromNode(ann);
889                            annotations.add(annot);
890                        } else {
891                            logger.severe("CREATED relation does not end with ANNOTATION: " + ann);
892                        }
893                    }
894                    tx.success();
895                }
896            }
897        }
898        // TODO: if both uri and user are given we should intersect
899
900        return annotations;
901    }
902
903    /**
904     * Returns Relationship of type from Node start to Node end. Creates one if
905     * it doesn't exist.
906     *
907     * @param start
908     * @param type
909     * @param end
910     * @return
911     */
912    protected Relationship getOrCreateRelation(Node start, RelationshipType type, Node end) {
913        if (start == null || end == null)
914            return null;
915        if (start.hasRelationship()) {
916            // there are relations
917            try (Transaction tx = graphDb.beginTx()) {
918                Iterable<Relationship> rels = start.getRelationships(type, Direction.OUTGOING);
919                for (Relationship rel : rels) {
920                    if (rel.getEndNode().equals(end)) {
921                        // relation exists
922                        tx.success();
923                        return rel;
924                    }
925                }
926                tx.success();
927            }
928        }
929        // create new one
930        Relationship rel;
931        try (Transaction tx = graphDb.beginTx()) {
932            rel = start.createRelationshipTo(end, type);
933            tx.success();
934        }
935        return rel;
936    }
937
938    protected Node getOrCreateAnnotationNode(String id) {
939        Index<Node> idx = getNodeIndex(NodeTypes.ANNOTATION);
940        Node annotation;
941        try (Transaction tx = graphDb.beginTx()) {
942            IndexHits<Node> annotations = idx.get("id", id);
943            annotation = annotations.getSingle();
944            if (annotation == null) {
945                // does not exist yet
946                annotation = graphDb.createNode();
947                annotation.setProperty("TYPE", NodeTypes.ANNOTATION.name());
948                annotation.setProperty("id", id);
949                idx.add(annotation, "id", id);
950            }
951            tx.success();
952        }
953        return annotation;
954    }
955
956    protected Node getOrCreateUriNode(String uri, NodeTypes type) {
957        Index<Node> idx = getNodeIndex(type);
958        Node target;
959        try (Transaction tx = graphDb.beginTx()) {
960            IndexHits<Node> targets = idx.get("uri", uri);
961            target = targets.getSingle();
962            if (target == null) {
963                // does not exist yet
964                target = graphDb.createNode();
965                target.setProperty("TYPE", type.name());
966                target.setProperty("uri", uri);
967                idx.add(target, "uri", uri);
968            }
969            tx.success();
970        }
971        return target;
972    }
973
974    protected Node getActorNode(Actor actor) {
975        // Person/Group is identified by URI or id
976        String uri = actor.getUriString();
977        Index<Node> idx;
978        Node person;
979        if (actor.isGroup()) {
980            idx = getNodeIndex(NodeTypes.GROUP);
981        } else {
982            idx = getNodeIndex(NodeTypes.PERSON);
983        }
984        try (Transaction tx = graphDb.beginTx()) {
985            IndexHits<Node> persons = idx.get("uri", uri);
986            person = persons.getSingle();
987            tx.success();
988        }
989        return person;
990    }
991
992    protected Node getOrCreateActorNode(Actor actor) {
993        // Person/Group is identified by URI or id
994        String uri = actor.getUriString();
995        String name = actor.getName();
996        String id = actor.getId();
997        Node person;
998        try (Transaction tx = graphDb.beginTx()) {
999            Index<Node> idx;
1000            if (actor.isGroup()) {
1001                idx = getNodeIndex(NodeTypes.GROUP);
1002            } else {
1003                idx = getNodeIndex(NodeTypes.PERSON);
1004            }
1005            IndexHits<Node> persons = idx.get("uri", uri);
1006            person = persons.getSingle();
1007            if (person == null) {
1008                // does not exist yet
1009                person = graphDb.createNode();
1010                if (actor.isGroup()) {
1011                    person.setProperty("TYPE", NodeTypes.GROUP.name());
1012                } else {
1013                    person.setProperty("TYPE", NodeTypes.PERSON.name());
1014                }
1015                person.setProperty("uri", uri);
1016                idx.add(person, "uri", uri);
1017                if (name != null) {
1018                    person.setProperty("name", name);
1019                }
1020                if (id != null) {
1021                    person.setProperty("id", id);
1022                }
1023            }
1024            tx.success();
1025        }
1026        return person;
1027    }
1028
1029    protected Node getOrCreateTagNode(Tag inTag) {
1030        Index<Node> idx = getNodeIndex(NodeTypes.TAG);
1031        String tagname = inTag.getName();
1032        Node tag;
1033        try (Transaction tx = graphDb.beginTx()) {
1034            IndexHits<Node> tags = idx.get("name", tagname);
1035            tag = tags.getSingle();
1036            if (tag == null) {
1037                // does not exist yet
1038                tag = graphDb.createNode();
1039                tag.setProperty("TYPE", NodeTypes.TAG.name());
1040                tag.setProperty("name", tagname);
1041                idx.add(tag, "name", tagname);
1042
1043                tag.setProperty("id", inTag.getId());
1044                tag.setProperty("uri", inTag.getUri());
1045                idx.add(tag, "uri", inTag.getUri());
1046            }
1047            tx.success();
1048        }
1049        return tag;
1050    }
1051
1052    /**
1053     * Create or update permissions relations for an annotation.
1054     *
1055     * @param annotNode
1056     * @param type
1057     * @param annot
1058     */
1059    protected void setPermissionRelation(Node annotNode, RelationTypes type, Actor actor) {
1060        Node newActorNode = null;
1061        if (actor != null) {
1062            newActorNode = getOrCreateActorNode(actor);
1063        }
1064        Relationship rel = getRelation(annotNode, type, null);
1065        if (rel != null) {
1066            // relation exists
1067            try (Transaction tx = graphDb.beginTx()) {
1068                Node oldActorNode = rel.getEndNode();
1069                if (!oldActorNode.equals(newActorNode)) {
1070                    // new admin is different
1071                    rel.delete();
1072                    tx.success();
1073                }
1074                if (newActorNode != null) {
1075                    rel = getOrCreateRelation(annotNode, type, newActorNode);
1076                }
1077                tx.success();
1078            }
1079        } else {
1080            // no relation yet
1081            if (newActorNode != null) {
1082                rel = getOrCreateRelation(annotNode, type, newActorNode);
1083            }
1084        }
1085    }
1086
1087    /**
1088     * Unindexes and deletes given Node if it has no relations.
1089     *
1090     * @param node
1091     */
1092    protected void deleteNode(Node node) {
1093        try (Transaction tx = graphDb.beginTx()) {
1094            if (node.hasRelationship()) {
1095                logger.severe("deleteNode: unable to delete: Node still has relations.");
1096            } else {
1097                String ts = (String) node.getProperty("TYPE", null);
1098                try {
1099                    NodeTypes type = NodeTypes.valueOf(ts);
1100                    getNodeIndex(type).remove(node);
1101                } catch (Exception e) {
1102                    logger.severe("deleteNode: unable to get TYPE of node: " + node);
1103                }
1104                node.delete();
1105            }
1106            tx.success();
1107        }
1108    }
1109
1110    /**
1111     * returns the (first) Relationship of RelationTypes type from Node start.
1112     *
1113     * @param start
1114     * @param type
1115     * @param direction
1116     * @return
1117     */
1118    protected Relationship getRelation(Node start, RelationTypes type, Direction direction) {
1119        Iterable<Relationship> rels;
1120        try (Transaction tx = graphDb.beginTx()) {
1121            if (direction == null) {
1122                // ignore direction
1123                rels = start.getRelationships(type);
1124            } else {
1125                rels = start.getRelationships(type, direction);
1126            }
1127            tx.success();
1128            for (Relationship rel : rels) {
1129                // just the first one
1130                return rel;
1131            }
1132        }
1133        return null;
1134    }
1135
1136    /**
1137     * Erzeuge eine urn aus der aktuellen Zeit in millis
1138     *
1139     * @return
1140     */
1141    private String createRessourceURI(String prefix) {
1142        Calendar cal = Calendar.getInstance();
1143        long time = cal.getTimeInMillis();
1144        return String.format("%s%s%s", ANNOTATION_URI_PREFIX, prefix, time);
1145    }
1146
1147    public List<Annotation> getAnnotationsByTag(String tagUri) {
1148        ArrayList<Annotation> ret = new ArrayList<Annotation>();
1149        Node tag = getTagNodeByUri(tagUri);
1150        if (tag != null) {
1151            try (Transaction tx = graphDb.beginTx()) {
1152                Iterable<Relationship> rels = tag.getRelationships(Direction.INCOMING, RelationTypes.HAS_TAG);
1153                for (Relationship rel : rels) {
1154                    Node node = rel.getStartNode();
1155                    ret.add(createAnnotationFromNode(node));
1156                }
1157                tx.success();
1158            }
1159        }
1160        return ret;
1161    }
1162
1163    public List<Annotation> getAnnotationsByResource(String resourceUri) {
1164        ArrayList<Annotation> ret = new ArrayList<Annotation>();
1165        Node res = getNodeFromIndex("uri", resourceUri, NodeTypes.RESOURCE);
1166        if (res != null) {
1167            try (Transaction tx = graphDb.beginTx()) {
1168                Iterable<Relationship> rels = res.getRelationships(Direction.INCOMING, RelationTypes.ANNOTATES);
1169                for (Relationship rel : rels) {
1170                    Node an = rel.getStartNode();
1171                    Node rn = rel.getEndNode();
1172                    if (rn.getProperty("TYPE", "").equals("RESOURCE")) {
1173                        logger.severe("getAnnotationsByResource got ANNOTATES != RESOURCE");
1174                    }
1175                    ret.add(createAnnotationFromNode(an));
1176                }
1177                tx.success();
1178            }
1179        }
1180        return ret;
1181    }
1182
1183}
Note: See TracBrowser for help on using the repository browser.