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