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