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