- changed type of BioCaseQuery results from List to Set to filter out duplicates
[taxeditor.git] / eu.etaxonomy.taxeditor.bulkeditor / src / main / java / eu / etaxonomy / taxeditor / annotatedlineeditor / AnnotatedLineDocumentProvider.java
1 // $Id$
2 /**
3 * Copyright (C) 2007 EDIT
4 * European Distributed Institute of Taxonomy
5 * http://www.e-taxonomy.eu
6 *
7 * The contents of this file are subject to the Mozilla Public License Version 1.1
8 * See LICENSE.TXT at the top of this package for the full license terms.
9 */
10 package eu.etaxonomy.taxeditor.annotatedlineeditor;
11
12 import java.util.ArrayList;
13 import java.util.Collection;
14 import java.util.Iterator;
15 import java.util.List;
16
17 import org.eclipse.core.runtime.Assert;
18 import org.eclipse.core.runtime.CoreException;
19 import org.eclipse.core.runtime.IProgressMonitor;
20 import org.eclipse.jface.operation.IRunnableContext;
21 import org.eclipse.jface.text.BadLocationException;
22 import org.eclipse.jface.text.Document;
23 import org.eclipse.jface.text.IDocument;
24 import org.eclipse.jface.text.Position;
25 import org.eclipse.jface.text.source.Annotation;
26 import org.eclipse.jface.text.source.IAnnotationModel;
27 import org.eclipse.ui.IEditorInput;
28 import org.eclipse.ui.texteditor.AbstractDocumentProvider;
29
30 import eu.etaxonomy.cdm.model.occurrence.SpecimenOrObservationBase;
31 import eu.etaxonomy.taxeditor.bulkeditor.input.AbstractBulkEditorInput;
32 import eu.etaxonomy.taxeditor.dataimport.DerivedUnitEditorInput;
33 import eu.etaxonomy.taxeditor.model.AbstractUtility;
34
35
36
37 /**
38 * Using an <code>IEditorPart</code>, creates a document where each line is associated
39 * with an entity. Mapping between document positions and entities is stored in
40 * <code>LineAnnotation</code>s in a <code>LineAnnotationModel</code>.
41 * <p>
42 * Requires:
43 * <ul>
44 * <li>an <code>IEntityCreator</code> to create entities for new lines;
45 * <li>an <code>IEntityPersistenceService</code> for interacting with the persistence layer; and
46 * <li>an <code>ILineDisplayStrategy</code> for various visual manifestations of the domain object.
47 * </ul>
48 *
49 * @author p.ciardelli
50 * @author n.hoffmann
51 * @created 25.06.2009
52 * @version 1.0
53 */
54 public class AnnotatedLineDocumentProvider extends AbstractDocumentProvider {
55
56 private IDocument document;
57
58 private LineAnnotationModel annotationModel;
59
60 private IEntityCreator<?> entityCreator;
61
62 private ILineDisplayStrategy lineDisplayStrategy;
63
64 private final IEditorInput input;
65
66 /**
67 * <p>Constructor for AnnotatedLineDocumentProvider.</p>
68 *
69 * @param input a {@link org.eclipse.ui.IEditorInput} object.
70 */
71 public AnnotatedLineDocumentProvider(IEditorInput input){
72 this.input = input;
73 }
74
75 /** {@inheritDoc} */
76 @Override
77 public IAnnotationModel getAnnotationModel(Object element) {
78 if (element == input) {
79
80 // Create model as necessary
81 if (annotationModel == null) {
82 annotationModel = new LineAnnotationModel(getLineDisplayStrategy(element));
83 annotationModel.setEntityCreator(getEntityCreator(input));
84 }
85 return annotationModel;
86 }
87
88 return null;
89 }
90
91 /** {@inheritDoc} */
92 @Override
93 protected IAnnotationModel createAnnotationModel(Object element)
94 throws CoreException {
95 return getAnnotationModel(element);
96 }
97
98 /** {@inheritDoc} */
99 @Override
100 public IDocument getDocument(Object element) {
101 return document;
102 }
103
104 /** {@inheritDoc} */
105 @Override
106 protected IDocument createDocument(Object element) throws CoreException {
107
108 if (element instanceof IEditorInput) {
109 IEditorInput input = (IEditorInput) element;
110 document = new Document("");
111 // IAnnotationModel model = getAnnotationModel(element);
112
113 List<?> entityList = getEntityList(element);
114
115 if(entityList != null){
116 for (Object entity : entityList) {
117 try {
118 createAnnotatedLine(input, entity);
119 } catch (BadLocationException e) {
120 AbstractUtility.error(getClass(), "Problems creating annotated line: ", e);
121 }
122 }
123 }
124
125 return document;
126 }
127 return null;
128 }
129
130 /**
131 * Creates an annotated line at the end of the document associated with the element
132 *
133 * @throws org.eclipse.jface.text.BadLocationException if any.
134 * @param element a {@link java.lang.Object} object.
135 * @param entity a {@link java.lang.Object} object.
136 * @return a {@link eu.etaxonomy.taxeditor.annotatedlineeditor.LineAnnotation} object.
137 */
138 protected LineAnnotation createAnnotatedLine(Object element, Object entity) throws BadLocationException {
139
140 Document document = (Document) getDocument(element);
141 ILineDisplayStrategy lineDisplayStrategy = getLineDisplayStrategy(element);
142
143 LineAnnotation annotation = new LineAnnotation(entity, lineDisplayStrategy);
144
145 // Is document zero length, or is last char in document line delimiter?
146 int docLength = document.getLength();
147 boolean useDelimiter = false;
148 if (docLength > 0) {
149 if (docLength > 1 && !document.get(docLength - 2, 2).equals(document.getDefaultLineDelimiter())) {
150 useDelimiter = true;
151 }
152 }
153 if (useDelimiter) {
154 document.replace(docLength, 0, document.getDefaultLineDelimiter());
155 }
156
157 String text;
158 if (lineDisplayStrategy.isEntityCacheEmpty(entity)) {
159 text = lineDisplayStrategy.getEmptyCacheMessage(entity);
160 } else {
161 text = lineDisplayStrategy.getText(entity);
162 }
163 text += lineDisplayStrategy.getSupplementalText(entity);
164 docLength = document.getLength();
165 document.replace(docLength, 0, text);
166
167 Position position = new Position(docLength, text.length());
168
169 IAnnotationModel model = getAnnotationModel(element);
170 if(model != null){
171 model.addAnnotation(annotation, position);
172 }
173
174 // lineDisplayStrategy.addDisplayListener(entity,
175 // new EntityListenerImpl((LineAnnotation) annotation, element));
176 //
177 return annotation;
178 }
179
180
181 /**
182 * <p>updateLineFromAnnotation</p>
183 *
184 * @param annotation a {@link eu.etaxonomy.taxeditor.annotatedlineeditor.LineAnnotation} object.
185 */
186 public void updateLineFromAnnotation(LineAnnotation annotation) {
187
188 IAnnotationModel model = getAnnotationModel(input);
189 IDocument document = getDocument(input);
190 ILineDisplayStrategy lineDisplay = getLineDisplayStrategy(input);
191
192 Object entity = annotation.getEntity();
193 String text = "";
194 if (getLineDisplayStrategy(input).isEntityCacheEmpty(entity)) {
195 text = lineDisplay.getEmptyCacheMessage(entity);
196 } else {
197 text = lineDisplay.getText(entity);
198 }
199 text += lineDisplay.getSupplementalText(entity);
200
201 try {
202 if (model.getPosition(annotation) == null) {
203 return;
204 }
205 int offset = model.getPosition(annotation).getOffset();
206 int line = document.getLineOfOffset(offset);
207 int lineLength = document.getLineLength(document.getLineOfOffset(offset));
208 if (document.getLineDelimiter(line) != null) {
209 lineLength -= document.getLineDelimiter(line).length();
210 }
211 document.replace(offset, lineLength, text);
212 } catch (BadLocationException e) {
213 AbstractUtility.error(getClass(), "Problem updating annotated line: " ,e);
214 }
215 }
216
217 /**
218 * @param element
219 * @return
220 */
221 private List<?> getEntityList(Object element) {
222 if (element instanceof AbstractBulkEditorInput) {
223 return ((AbstractBulkEditorInput)element).getModel();
224 }
225 else if(element instanceof DerivedUnitEditorInput){
226 Collection<SpecimenOrObservationBase> results = ((DerivedUnitEditorInput) element).getResults();
227 if(results!=null){
228 return new ArrayList<SpecimenOrObservationBase>(results);
229 }
230 }
231 return null;
232 }
233
234 /** {@inheritDoc} */
235 @Override
236 protected void doSaveDocument(IProgressMonitor monitor, Object element,
237 IDocument document, boolean overwrite) throws CoreException {
238 if (element instanceof AbstractBulkEditorInput) {
239
240 IEntityPersistenceService persistenceService = (AbstractBulkEditorInput) element;
241
242 // Get new containers from annotation model
243 LineAnnotationModel model = (LineAnnotationModel) getAnnotationModel(element);
244 Iterator iterator = model.getAnnotationIterator();
245 while (iterator.hasNext()) {
246 Annotation annotation = (Annotation) iterator.next();
247 if (annotation instanceof IEntityContainer<?>) {
248 IEntityContainer<?> container = (IEntityContainer<?>) annotation;
249 if (container.isMarkedAsNew() || container.isDirty()) {
250 persistenceService.save(container.getEntity()); // save
251 container.setDirty(false);
252 container.markAsNew(false);
253 }
254 }
255 }
256 for (LineAnnotation annotation : model.getDeletedAnnotations()) {
257 if (annotation.isMarkedAsNew()) {
258 continue;
259 }
260 if (annotation.isMarkedAsMerged()) {
261 persistenceService.merge(annotation.getEntity(), annotation.getMergeTarget()); // merge
262 } else {
263 // TODO clarify w AM whether this needs to be executed on merged objects
264 //persistenceService.delete(annotation.getEntity()); // delete
265 }
266 }
267 model.clearDeletedAnnotations();
268 }
269 }
270
271 /** {@inheritDoc} */
272 @Override
273 protected IRunnableContext getOperationRunner(IProgressMonitor monitor) {
274 return null;
275 }
276
277 /** {@inheritDoc} */
278 @Override
279 public boolean isModifiable(Object element) {
280 return true;
281 }
282
283 /** {@inheritDoc} */
284 @Override
285 public boolean isReadOnly(Object element) {
286 // enables copy & paste
287 return false;
288 }
289
290 /**
291 * <p>Getter for the field <code>entityCreator</code>.</p>
292 *
293 * @param element a {@link java.lang.Object} object.
294 * @return a {@link eu.etaxonomy.taxeditor.annotatedlineeditor.IEntityCreator} object.
295 */
296 public IEntityCreator<?> getEntityCreator(IEditorInput input) {
297 if (input instanceof AbstractBulkEditorInput) {
298 entityCreator = ((AbstractBulkEditorInput) input).getEntityCreator();
299 }
300 return entityCreator;
301 }
302
303 /**
304 * <p>Setter for the field <code>lineDisplayStrategy</code>.</p>
305 *
306 * @param lineDisplayStrategy a {@link eu.etaxonomy.taxeditor.annotatedlineeditor.ILineDisplayStrategy} object.
307 * @param element a {@link java.lang.Object} object.
308 */
309 public void setLineDisplayStrategy(
310 ILineDisplayStrategy lineDisplayStrategy, Object element) {
311 if (element instanceof IEditorInput) {
312 this.lineDisplayStrategy = lineDisplayStrategy;
313 }
314 }
315
316 /**
317 * <p>Getter for the field <code>lineDisplayStrategy</code>.</p>
318 *
319 * @param element a {@link java.lang.Object} object.
320 * @return a {@link eu.etaxonomy.taxeditor.annotatedlineeditor.ILineDisplayStrategy} object.
321 */
322 protected ILineDisplayStrategy getLineDisplayStrategy(Object element) {
323 if (element instanceof IEditorInput) {
324 Assert.isNotNull(lineDisplayStrategy, "No ILineDisplayStrategy set for this element.");
325 return lineDisplayStrategy;
326 }
327 return null;
328 }
329
330 /**
331 * <p>removeAnnotatedLine</p>
332 *
333 * @param entity a {@link java.lang.Object} object.
334 */
335 public void removeAnnotatedLine(Object entity){
336 LineAnnotation annotation = (LineAnnotation) annotationModel.getAnnotation(entity);
337 removeAnnotatedLine(annotation);
338 }
339
340 /**
341 * <p>removeAnnotatedLine</p>
342 *
343 * @param lineno a int.
344 * @param element a {@link java.lang.Object} object.
345 */
346 public void removeAnnotatedLine(Object element, int lineno) {
347 LineAnnotation annotation = (LineAnnotation) annotationModel.getAnnotationAtLine(lineno, document);
348 removeAnnotatedLine(annotation);
349 }
350
351 /**
352 * <p>removeAnnotatedLine</p>
353 *
354 * @param annotation a {@link eu.etaxonomy.taxeditor.annotatedlineeditor.LineAnnotation} object.
355 */
356 public void removeAnnotatedLine(LineAnnotation annotation) {
357 if (annotation != null) {
358 Document document = (Document) getDocument(input);
359 LineAnnotationModel model = (LineAnnotationModel) getAnnotationModel(input);
360
361 Position position = model.getPosition(annotation);
362 int offset = position.getOffset();
363 int length = position.getLength();
364
365 Object entity = annotation.getEntity();
366 annotation.markAsDeleted();
367 model.removeAnnotation(annotation);
368
369 // Immediately followed by a delimiter?
370 int annotationEnd = offset + length;
371 try {
372 if (document.getLength() > annotationEnd + 1 && document.get(annotationEnd, 2).equals(document.getDefaultLineDelimiter())) {
373 length += 2;
374 }
375 } catch (BadLocationException e1) {
376 AbstractUtility.error(getClass(), "Problems removing annotated line", e1);
377 }
378
379 try {
380 document.replace(offset, length, "");
381 } catch (BadLocationException e) {
382 AbstractUtility.error(getClass(), "Problems removing annotated line", e);
383 }
384 }
385 }
386
387 /* (non-Javadoc)
388 * @see org.eclipse.ui.texteditor.AbstractDocumentProvider#changed(java.lang.Object)
389 */
390 /** {@inheritDoc} */
391 @Override
392 public void changed(Object entity) {
393 LineAnnotation annotation = (LineAnnotation) annotationModel.getAnnotation(entity);
394 if(annotation != null){
395 annotation.setDirty(true);
396 updateLineFromAnnotation(annotation);
397 }
398 }
399 }