source: DASISH/t5.6/backend/annotator-backend/trunk/annotator-backend/src/main/java/eu/dasish/annotation/backend/dao/impl/JdbcAnnotationDao.java @ 3370

Last change on this file since 3370 was 3370, checked in by olhsha, 11 years ago

filtered getting annotation is implemented and tested (except filtering by the logged-in user access mode)

File size: 16.4 KB
Line 
1/*
2 * Copyright (C) 2013 DASISH
3 *
4 * This program is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU General Public License
6 * as published by the Free Software Foundation; either version 2
7 * of the License, or (at your option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12 * GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software
16 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
17 */
18package eu.dasish.annotation.backend.dao.impl;
19
20import eu.dasish.annotation.backend.Helpers;
21import eu.dasish.annotation.backend.dao.AnnotationDao;
22import eu.dasish.annotation.backend.dao.NotebookDao;
23import eu.dasish.annotation.backend.dao.PermissionsDao;
24import eu.dasish.annotation.backend.dao.SourceDao;
25import eu.dasish.annotation.backend.dao.UserDao;
26import eu.dasish.annotation.backend.identifiers.AnnotationIdentifier;
27import eu.dasish.annotation.backend.identifiers.UserIdentifier;
28import eu.dasish.annotation.schema.Annotation;
29import eu.dasish.annotation.schema.AnnotationBody;
30import eu.dasish.annotation.schema.AnnotationInfo;
31import eu.dasish.annotation.schema.NewOrExistingSourceInfo;
32import eu.dasish.annotation.schema.NewOrExistingSourceInfos;
33import eu.dasish.annotation.schema.NewSourceInfo;
34import eu.dasish.annotation.schema.ResourceREF;
35import eu.dasish.annotation.schema.SourceInfo;
36import java.sql.ResultSet;
37import java.sql.SQLException;
38import java.sql.Timestamp;
39import java.util.ArrayList;
40import java.util.HashMap;
41import java.util.List;
42import java.util.Map;
43import javax.sql.DataSource;
44import javax.xml.datatype.DatatypeConfigurationException;
45import org.springframework.beans.factory.annotation.Autowired;
46import org.springframework.dao.DataAccessException;
47import org.springframework.jdbc.core.RowMapper;
48
49/**
50 * Created on : Jun 27, 2013, 10:30:52 AM
51 *
52 * @author Peter Withers <peter.withers@mpi.nl>
53 */
54public class JdbcAnnotationDao extends JdbcResourceDao implements AnnotationDao {
55
56    @Autowired
57    PermissionsDao jdbcPermissionsDao;
58    @Autowired
59    UserDao jdbcUserDao;
60    @Autowired
61    NotebookDao jdbcNotebookDao;
62    @Autowired
63    SourceDao jdbcSourceDao;
64
65    public JdbcAnnotationDao(DataSource dataSource) {
66        setDataSource(dataSource);
67        internalIdName = annotation_id;
68        resourceTableName = annotationTableName;
69    }
70
71    @Override
72    public List<Number> getFilteredAnnotationIDs(String link, String text, String access, String namespace, UserIdentifier owner, Timestamp after, Timestamp before) {
73       
74        StringBuilder sql = new StringBuilder("SELECT DISTINCT ");
75        sql.append(annotation_id).append(" FROM ").append(annotationTableName).append(" WHERE TRUE ");       
76        Map<String, Object> params = new HashMap<String, Object>();
77       
78         if (link != null) {
79            List<Number> sourceIDs = jdbcSourceDao.getSourcesForLink(link);
80            List<Number> annotationIDs = getAnnotationIDsForSources(sourceIDs);
81            if (!annotationIDs.isEmpty()) {
82                String values = makeListOfValues(annotationIDs);
83                sql.append(" AND ").append(annotation_id).append(" IN ").append(values);
84            }
85            else{
86                return new ArrayList<Number>();
87            }
88        }
89         
90
91        if (owner != null) {
92            Number ownerID = jdbcUserDao.getInternalID(owner);
93            sql.append(" AND ").append(owner_id).append(" = :owner ");
94            params.put("owner", ownerID);
95        }
96
97        if (after != null) {
98            sql.append(" AND ").append(time_stamp).append("  > :after");
99            params.put("after", after);
100        }
101
102        if (before != null) {
103            sql.append(" AND ").append(time_stamp).append("  < :before");           
104            params.put("before", before);
105        }
106
107        if (text != null) {
108            sql.append(" AND ").append(body_xml).append("  LIKE '%").append(text).append("%'");
109        }
110       
111       
112        List<Number> result = getSimpleJdbcTemplate().query(sql.toString(), internalIDRowMapper, params);
113        return result;
114    }
115   
116    //////////////////////////////
117   
118    @Override
119    public List<Number> getAnnotationIDsForSources(List<Number> sourceIDs){       
120        if (sourceIDs == null) {
121            return null;
122        }       
123        if (sourceIDs.isEmpty()) {
124           return new ArrayList<Number>(); 
125        }       
126        String values = makeListOfValues(sourceIDs);
127        StringBuilder query = new StringBuilder("SELECT DISTINCT ");
128        query.append(annotation_id).append(" FROM ").append(annotationsSourcesTableName).append(" WHERE ").append(source_id).append(" IN ");
129        query.append(values);
130        List<Number> result = getSimpleJdbcTemplate().query(query.toString(), internalIDRowMapper);
131        return result;
132    }
133           
134   
135
136    @Override
137    public List<AnnotationInfo> getAnnotationInfos(List<Number> annotationIDs) {
138
139        if (annotationIDs == null) {
140            return null;
141        }
142
143        if (annotationIDs.isEmpty()) {
144            return (new ArrayList<AnnotationInfo>());
145        }
146
147        String values = makeListOfValues(annotationIDs);
148        String sql = "SELECT " + annotationStar + " FROM " + annotationTableName + " WHERE " + annotationAnnotation_id + "  IN " + values;
149        return getSimpleJdbcTemplate().query(sql, annotationInfoRowMapper);
150    }
151    private final RowMapper<AnnotationInfo> annotationInfoRowMapper = new RowMapper<AnnotationInfo>() {
152        @Override
153        public AnnotationInfo mapRow(ResultSet rs, int rowNumber) throws SQLException {
154            AnnotationInfo annotationInfo = new AnnotationInfo();
155            annotationInfo.setOwner(getResourceREF(Integer.toString(rs.getInt(owner_id))));
156            annotationInfo.setHeadline(rs.getString(headline));
157            return annotationInfo;
158        }
159    };
160
161    /////////////////////////////////////////////////
162    /**
163     *
164     * @param annotationIDs
165     * @return list of annotation references corresponding to the annotation-ids
166     * from the input list if the input list is null or empty (zero elements)
167     * returns an empty list
168     * there may be annotationIDs which are not in the DB (so that's why we need this method).
169     */
170   
171    @Override
172    public List<ResourceREF> getAnnotationREFs(List<Number> annotationIDs) {
173
174        if (annotationIDs == null) {
175            return null;
176        }
177
178        if (annotationIDs.isEmpty()) {
179            return (new ArrayList<ResourceREF>());
180        }
181
182        String values = makeListOfValues(annotationIDs);
183        String sql = "SELECT " + annotationAnnotation_id + " FROM " + annotationTableName + " WHERE " + annotationAnnotation_id + "  IN " + values;
184        return getSimpleJdbcTemplate().query(sql, annotationREFRowMapper);
185    }
186    private final RowMapper<ResourceREF> annotationREFRowMapper = new RowMapper<ResourceREF>() {
187        @Override
188        public ResourceREF mapRow(ResultSet rs, int rowNumber) throws SQLException {
189            ResourceREF annotationREF = new ResourceREF();
190            annotationREF.setRef(Integer.toString(rs.getInt(annotation_id)));
191            return annotationREF;
192        }
193    };
194
195    //////////////////////////////////////////////////////////////////////////
196    @Override
197    public Annotation getAnnotation(Number annotationID) throws SQLException {
198        if (annotationID == null) {
199            return null;
200        }
201        String sql = "SELECT " + annotationStar + " FROM " + annotationTableName + " WHERE " + annotationAnnotation_id + "= ?";
202        List<Annotation> result = getSimpleJdbcTemplate().query(sql, annotationRowMapper, annotationID);
203
204        if (result == null) {
205            return null;
206        }
207        if (result.isEmpty()) {
208            return null;
209        }
210        return result.get(0);
211    }
212    private final RowMapper<Annotation> annotationRowMapper = new RowMapper<Annotation>() {
213        @Override
214        public Annotation mapRow(ResultSet rs, int rowNumber) throws SQLException {
215            Annotation result = new Annotation();
216
217            ResourceREF ownerREF = new ResourceREF();
218            ownerREF.setRef(String.valueOf(rs.getInt(owner_id)));
219            result.setOwner(ownerREF);
220
221            result.setHeadline(rs.getString(headline));
222
223            result.setBody(convertToAnnotationBody(rs.getString(body_xml)));
224
225            List<SourceInfo> sourceInfoList = jdbcSourceDao.getSourceInfos(rs.getInt(annotation_id));
226            NewOrExistingSourceInfos noeSourceInfos = jdbcSourceDao.contructNewOrExistingSourceInfo(sourceInfoList);
227            result.setTargetSources(noeSourceInfos);
228
229            // TODO: fix: rpelace URI in the schema with external id, or make here the conversion:
230            // from external ID in the DB to the URI for the class
231            result.setURI(rs.getString(external_id));
232
233            try {
234                result.setTimeStamp(Helpers.setXMLGregorianCalendar(rs.getTimestamp(time_stamp)));
235                return result;
236            } catch (DatatypeConfigurationException e) {
237                System.out.println(e);
238                return result; // no date-time is set
239            }
240        }
241    };
242
243    private AnnotationBody convertToAnnotationBody(String input) {
244        if (input == null) {
245            return null;
246        }
247
248        AnnotationBody result = new AnnotationBody();
249        List<Object> element = result.getAny();
250        element.add(input);
251        return result;
252    }
253
254    @Override
255    public int deleteAnnotation(Number annotationId) throws SQLException {
256
257        String sqlNotebooks = "DELETE FROM " + notebooksAnnotationsTableName + " where " + annotation_id + " = ?";
258        int affectedNotebooks = getSimpleJdbcTemplate().update(sqlNotebooks, annotationId);
259
260        String sqlPermissions = "DELETE FROM " + permissionsTableName + " where " + annotation_id + " = ?";
261        int affectedPermissions = getSimpleJdbcTemplate().update(sqlPermissions, annotationId);
262
263        // safe removing sources
264        List<Number> sourceIDs = jdbcSourceDao.retrieveSourceIDs(annotationId);
265        String sqlTargetSources = "DELETE FROM " + annotationsSourcesTableName + " where " + annotation_id + " = ?";
266        int affectedAnnotationsSources = getSimpleJdbcTemplate().update(sqlTargetSources, annotationId);
267        int affectedSources;
268        for (Number sourceID : sourceIDs) {
269            // call  the method in sources DAO that handles removal of a source which is not refered by other annotations
270            affectedSources = jdbcSourceDao.deleteSource(sourceID);
271        }
272
273        String sqlAnnotation = "DELETE FROM " + annotationTableName + " where " + annotation_id + " = ?";
274        int affectedAnnotations = getSimpleJdbcTemplate().update(sqlAnnotation, annotationId);
275        if (affectedAnnotations > 1) {
276            throw new SQLException("There was more than one annotation (" + affectedAnnotations + ") with the same ID " + annotationId);
277        }
278        return affectedAnnotations;
279        //TODO implement deleting sources (see the specification document and the interfaces' javadoc
280    }
281
282    // TODO: so far URI in the xml is the same as the external_id in the DB!!
283    // Change it when the decision is taken!!!
284    @Override
285    public Annotation addAnnotation(Annotation annotation, Number ownerID) throws SQLException {
286
287        Annotation result = makeDeepCopy(annotation);
288
289        ResourceREF ownerRef = new ResourceREF();
290        ownerRef.setRef(String.valueOf(ownerID));
291        result.setOwner(ownerRef);
292
293        // generate a new annotation ID
294        AnnotationIdentifier annotationIdentifier = new AnnotationIdentifier();
295        result.setURI(annotationIdentifier.toString());
296
297        Map<String, Object> params = new HashMap<String, Object>();
298        params.put("externalId", annotationIdentifier.toString());
299        //params.put("timeStamp", annotation.getTimeStamp()); is generated while adding the annotation in the DB as "now"
300        params.put("ownerId", ownerID);
301        params.put("headline", annotation.getHeadline());
302        params.put("bodyXml", annotation.getBody().getAny().get(0).toString());
303
304        try {
305
306            String sql = "INSERT INTO " + annotationTableName + "(" + external_id + "," + owner_id + "," + headline + "," + body_xml + " ) VALUES (:externalId, :ownerId, :headline, :bodyXml)";
307            final int affectedRows = getSimpleJdbcTemplate().update(sql, params);
308
309            if (affectedRows != 1) {
310                throw (new SQLException("Cannot add the annotation properly"));
311            }
312
313            Number internalID = getInternalID(annotationIdentifier);
314
315            //retrieve taime stamp for the just added annotation
316            result.setTimeStamp(retrieveTimeStamp(internalID));
317
318            // place new target sources in the DB, when necessary, update the corresponding target source info for the result
319            // the joint annotations_target_sources" tabel is updated.
320            List<NewOrExistingSourceInfo> sources = result.getTargetSources().getTarget();
321            Map<NewOrExistingSourceInfo, NewOrExistingSourceInfo> sourcePairs = jdbcSourceDao.addTargetSources(internalID, sources);
322            sources.clear();
323            sources.addAll(sourcePairs.values());
324
325            //replace the temporary sourceId-references in the body with the persistent externalId
326            String body = annotation.getBody().getAny().get(0).toString();
327            String newBody = updateTargetRefsInBody(body, sourcePairs);
328            List<Object> bodyXML = result.getBody().getAny();
329            bodyXML.clear();
330            bodyXML.add(newBody);
331            String sqlUpdate = "UPDATE " + annotationTableName + " SET " + body_xml + "= ? WHERE " + annotation_id + "= " + internalID;
332            int affectedRowsBodyUpd = getSimpleJdbcTemplate().update(sqlUpdate, newBody);
333            if (affectedRows != 1) {
334                throw (new SQLException("Cannot update the body with persistent reference ID"));
335            }
336
337            return result;
338        } catch (DataAccessException exception) {
339            throw exception;
340        }
341    }
342
343    //////////////////////////////////////////////////
344    @Override
345    public AnnotationIdentifier getExternalID(Number internalID) {
346        return new AnnotationIdentifier(super.getExternalIdentifier(internalID));
347    }
348
349    //////////// helpers ///////////////////////
350    /////////////////////////////////////////////////
351    private String updateTargetRefsInBody(String body, Map<NewOrExistingSourceInfo, NewOrExistingSourceInfo> sourcePairs) {
352        String result = body;
353        for (NewOrExistingSourceInfo tempSource : sourcePairs.keySet()) {
354            NewSourceInfo newSource = tempSource.getNewSource();
355            if (newSource != null) {
356                result = result.replaceAll(newSource.getId(), sourcePairs.get(tempSource).getSource().getRef());
357            }
358        }
359        return result;
360    }
361
362    ///////////////////////////////////////////////////////////
363    private ResourceREF getResourceREF(String resourceID) {
364        ResourceREF result = new ResourceREF();
365        result.setRef(resourceID);
366        return result;
367    }
368
369    ////////////////////////////////////////// 
370    private Annotation makeDeepCopy(Annotation annotation) {
371
372        if (annotation == null) {
373            return null;
374        }
375
376        Annotation result = new Annotation();
377
378        AnnotationBody body = new AnnotationBody();
379        String bodyString = annotation.getBody().getAny().get(0).toString();
380        body.getAny().add(bodyString);
381        result.setBody(body);
382
383        result.setHeadline(annotation.getHeadline());
384
385        ResourceREF owner = new ResourceREF();
386        owner.setRef(annotation.getOwner().getRef());
387        result.setOwner(owner);
388
389//        ResourceREF permissions = new ResourceREF();
390//        permissions.setRef(annotation.getPermissions().getRef());
391//        result.setPermissions(permissions);
392
393        result.setPermissions(null); //we do not have permissions there
394
395        NewOrExistingSourceInfos noesi = new NewOrExistingSourceInfos();
396        noesi.getTarget().addAll(annotation.getTargetSources().getTarget());
397        result.setTargetSources(noesi);
398
399        result.setTimeStamp(annotation.getTimeStamp());
400        result.setURI(annotation.getURI());
401
402        return result;
403    }
404}
Note: See TracBrowser for help on using the repository browser.