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