.
[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.HashMap;
13 import java.util.Iterator;
14 import java.util.List;
15 import java.util.Map;
16
17 import org.apache.log4j.Logger;
18 import org.eclipse.core.runtime.Assert;
19 import org.eclipse.core.runtime.CoreException;
20 import org.eclipse.core.runtime.IProgressMonitor;
21 import org.eclipse.jface.operation.IRunnableContext;
22 import org.eclipse.jface.text.BadLocationException;
23 import org.eclipse.jface.text.Document;
24 import org.eclipse.jface.text.IDocument;
25 import org.eclipse.jface.text.Position;
26 import org.eclipse.jface.text.source.Annotation;
27 import org.eclipse.jface.text.source.IAnnotationModel;
28 import org.eclipse.swt.SWT;
29 import org.eclipse.swt.custom.StyleRange;
30 import org.eclipse.ui.IEditorInput;
31 import org.eclipse.ui.texteditor.AbstractDocumentProvider;
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>LineAnnotationModdel</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 * @created 25.06.2009
48 * @version 1.0
49 */
50 public class AnnotatedLineDocumentProvider extends AbstractDocumentProvider {
51
52 private static final Logger logger = Logger
53 .getLogger(AnnotatedLineDocumentProvider.class);
54
55 private Map<IEditorInput, IDocument> documents =
56 new HashMap<IEditorInput, IDocument>();
57
58 private Map<IEditorInput, IAnnotationModel> annotationModels =
59 new HashMap<IEditorInput, IAnnotationModel>();
60
61 private Map<IEditorInput, IEntityCreator<?>> entityCreators =
62 new HashMap<IEditorInput, IEntityCreator<?>>();
63
64 private Map<IEditorInput, IEntityPersistenceService> persistenceServices =
65 new HashMap<IEditorInput, IEntityPersistenceService>();
66
67 private Map<IEditorInput, ILineDisplayStrategy> lineDisplayStrategies =
68 new HashMap<IEditorInput, ILineDisplayStrategy>();
69
70 // private ITextViewer viewer;
71 //
72 // public AnnotatedLineEditorDocumentProvider(ITextViewer viewer) {
73 // this.viewer = viewer;
74 // }
75
76 @Override
77 public IAnnotationModel getAnnotationModel(Object element) {
78 if (element instanceof IEditorInput) {
79
80 IEditorInput input = (IEditorInput) element;
81 IAnnotationModel model = annotationModels.get((IEditorInput) element);
82
83 // Create model as necessary
84 if (model == null) {
85 model = new LineAnnotationModel(getLineDisplayStrategy(element));
86 ((LineAnnotationModel) model).setEntityCreator(getEntityCreator(element));
87 annotationModels.put(input, model);
88 }
89 return model;
90 }
91
92 return null;
93 }
94
95 @Override
96 protected IAnnotationModel createAnnotationModel(Object element)
97 throws CoreException {
98 return getAnnotationModel(element);
99 }
100
101 @Override
102 public IDocument getDocument(Object element) {
103 if (element instanceof IEditorInput) {
104 return documents.get((IEditorInput) element);
105 }
106 return null;
107 }
108
109 @Override
110 protected IDocument createDocument(Object element) throws CoreException {
111
112 if (element instanceof IEditorInput) {
113 IEditorInput input = (IEditorInput) element;
114 Document document = new Document("");
115 IAnnotationModel model = getAnnotationModel(element);
116 documents.put(input, document);
117
118 for (Object entity : getEntityList(element)) {
119
120 try {
121 createAnnotatedLine(input, entity);
122 } catch (BadLocationException e) {
123 // TODO Auto-generated catch block
124 e.printStackTrace();
125 }
126 }
127 return document;
128 }
129 return null;
130 }
131
132 /**
133 * Creates an annotated line at the end of the document associated with the element
134 * @return
135 * @throws BadLocationException
136 */
137 protected LineAnnotation createAnnotatedLine(Object element, Object entity) throws BadLocationException {
138
139 Document document = (Document) getDocument(element);
140 ILineDisplayStrategy lineDisplay = getLineDisplayStrategy(element);
141
142 LineAnnotation annotation = new LineAnnotation(entity, lineDisplay);
143
144 // Is document zero length, or is last char in document line delimiter?
145 int docLength = document.getLength();
146 boolean useDelimiter = false;
147 if (docLength > 0) {
148 if (docLength > 1 && !document.get(docLength - 2, 2).equals(document.getDefaultLineDelimiter())) {
149 useDelimiter = true;
150 }
151 }
152 if (useDelimiter) {
153 document.replace(docLength, 0, document.getDefaultLineDelimiter());
154 }
155
156 String text;
157 boolean doItalics = false;
158 if (lineDisplay.isEntityCacheEmpty(entity)) {
159 text = lineDisplay.getEmptyCacheMessage(entity);
160 doItalics = true;
161 } else {
162 text = lineDisplay.getText(entity);
163 }
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 model.addAnnotation(annotation, position);
171
172 logger.info("Annotation added " + entity);
173
174 if (doItalics) {
175
176 // TODO implement italics for non-named objects in presentation repairer � la
177 // http://www.wsmostudio.org/multiproject/org.wsmostudio.grounding.wsdls/xref/org/wsmostudio/grounding/sawsdl/ui/text/NonRuleBasedDamagerRepairer.html
178
179 StyleRange styleRange = new StyleRange();
180 styleRange.start = docLength;
181 styleRange.length = text.length();
182 styleRange.fontStyle = SWT.ITALIC;
183 }
184
185 lineDisplay.addDisplayListener(entity,
186 new EntityListenerImpl((LineAnnotation) annotation, element, lineDisplay));
187
188 return annotation;
189 }
190
191 public void updateLineFromAnnotation(Object element, LineAnnotation annotation, String text) {
192 IAnnotationModel model = getAnnotationModel(element);
193 IDocument document = getDocument(element);
194 try {
195 if (model.getPosition(annotation) == null) {
196 return;
197 }
198 int offset = model.getPosition(annotation).getOffset();
199 int line = document.getLineOfOffset(offset);
200 int lineLength = document.getLineLength(document.getLineOfOffset(offset));
201 if (document.getLineDelimiter(line) != null) {
202 lineLength -= document.getLineDelimiter(line).length();
203 }
204 document.replace(offset, lineLength, text);
205 } catch (BadLocationException e) {
206 // TODO Auto-generated catch block
207 e.printStackTrace();
208 }
209 }
210
211 class EntityListenerImpl extends EntityListener {
212
213 private LineAnnotation annotation;
214 private ILineDisplayStrategy lineDisplay;
215 private Object element;
216
217 EntityListenerImpl(LineAnnotation annotation, Object element, ILineDisplayStrategy lineDisplay) {
218 this.annotation = annotation;
219 this.element = element;
220 this.lineDisplay = lineDisplay;
221 }
222 /* (non-Javadoc)
223 * @see eu.etaxonomy.taxeditor.annotatedlineeditor.EntityDisplayListener#updateDisplay()
224 */
225 @Override
226 protected void updateEntity() {
227
228 annotation.setDirty(true);
229
230 Object entity = annotation.getEntity();
231 String text;
232 if (lineDisplay.isEntityCacheEmpty(entity)) {
233 text = lineDisplay.getEmptyCacheMessage(entity);
234 } else {
235 text = lineDisplay.getText(entity);
236 }
237
238 updateLineFromAnnotation(element, annotation, text);
239 }
240 }
241
242 /**
243 * @param element
244 * @return
245 */
246 private List<?> getEntityList(Object element) {
247 if (element instanceof IEditorInput) {
248 return getPersistenceService(element).getEntityList(element);
249 }
250 return null;
251 }
252
253 @Override
254 protected void doSaveDocument(IProgressMonitor monitor, Object element,
255 IDocument document, boolean overwrite) throws CoreException {
256 if (element instanceof IEditorInput) {
257
258 IEntityPersistenceService persistenceService = getPersistenceService(element);
259
260 // Get new containers from annotation model
261 LineAnnotationModel model = (LineAnnotationModel) getAnnotationModel(element);
262 Iterator iterator = model.getAnnotationIterator();
263 while (iterator.hasNext()) {
264 Annotation annotation = (Annotation) iterator.next();
265 if (annotation instanceof IEntityContainer<?>) {
266 IEntityContainer<?> container = (IEntityContainer<?>) annotation;
267 if (container.isMarkedAsNew() || container.isDirty()) {
268 persistenceService.save(container.getEntity(), element); // save
269 container.setDirty(false);
270 container.markAsNew(false);
271 }
272 }
273 }
274 for (LineAnnotation annotation : model.getDeletedAnnotations()) {
275 if (annotation.isMarkedAsNew()) {
276 continue;
277 }
278 if (annotation.isMarkedAsMerged()) {
279 persistenceService.merge(annotation.getEntity(), annotation.getMergeTarget(), element); // merge
280 } else {
281 // TODO clarify w AM whether this needs to be executed on merged objects
282 persistenceService.delete(annotation.getEntity(), element); // delete
283 }
284 }
285 model.clearDeletedAnnotations();
286 }
287 }
288
289 @Override
290 protected IRunnableContext getOperationRunner(IProgressMonitor monitor) {
291 return null;
292 }
293
294 @Override
295 public boolean isModifiable(Object element) {
296 return true;
297 }
298
299 @Override
300 public boolean isReadOnly(Object element) {
301 // enables copy & paste
302 return false;
303 }
304
305 /**
306 * @param entityCreator
307 * @param element
308 */
309 public void setEntityCreator(IEntityCreator<?> entityCreator, Object element) {
310 if (element instanceof IEditorInput) {
311 entityCreators.put((IEditorInput) element, entityCreator);
312 }
313 }
314
315 /**
316 * @param element
317 * @return
318 */
319 public IEntityCreator<?> getEntityCreator(Object element) {
320 if (element instanceof IEditorInput) {
321 IEntityCreator<?> entityCreator = entityCreators.get(element);
322 Assert.isNotNull(entityCreator, "No IEntityCreator set for this element.");
323 return entityCreator;
324 }
325 return null;
326 }
327
328 /**
329 * @param persistenceService
330 * @param input
331 */
332 public void setPersistenceService(
333 IEntityPersistenceService persistenceService, Object element) {
334 if (element instanceof IEditorInput) {
335 persistenceServices.put((IEditorInput) element, persistenceService);
336 }
337 }
338
339 public IEntityPersistenceService getPersistenceService(Object element) {
340 if (element instanceof IEditorInput) {
341 IEntityPersistenceService persistenceService = persistenceServices.get((IEditorInput) element);
342 Assert.isNotNull(persistenceService, "No IEntityPersistenceService set for this element.");
343 return persistenceService;
344 }
345 return null;
346 }
347
348 /**
349 * @param lineDisplayStrategy
350 * @param input
351 */
352 public void setLineDisplayStrategy(
353 ILineDisplayStrategy lineDisplayStrategy, Object element) {
354 if (element instanceof IEditorInput) {
355 lineDisplayStrategies.put((IEditorInput) element, lineDisplayStrategy);
356 }
357 }
358
359 /**
360 * @param element
361 * @return
362 */
363 protected ILineDisplayStrategy getLineDisplayStrategy(Object element) {
364 if (element instanceof IEditorInput) {
365 ILineDisplayStrategy lineDisplayStrategy = lineDisplayStrategies.get((IEditorInput) element);
366 Assert.isNotNull(lineDisplayStrategy, "No ILineDisplayStrategy set for this element.");
367 return lineDisplayStrategy;
368 }
369 return null;
370 }
371
372 /**
373 * @param editorInput
374 * @param lineno
375 */
376 public void removeAnnotatedLine(Object element, int lineno) {
377 Document document = (Document) getDocument(element);
378 LineAnnotationModel model = (LineAnnotationModel) getAnnotationModel(element);
379 LineAnnotation annotation = (LineAnnotation) model.getAnnotationAtLine(lineno, document);
380 removeAnnotatedLine(element, annotation);
381 }
382
383 public void removeAnnotatedLine(Object element, LineAnnotation annotation) {
384 if (annotation != null) {
385 Document document = (Document) getDocument(element);
386 LineAnnotationModel model = (LineAnnotationModel) getAnnotationModel(element);
387
388 Position position = model.getPosition(annotation);
389 int offset = position.getOffset();
390 int length = position.getLength();
391
392 Object entity = annotation.getEntity();
393 annotation.markAsDeleted();
394 model.removeAnnotation(annotation);
395
396 // Immediately followed by a delimiter?
397 int annotationEnd = offset + length;
398 try {
399 if (document.getLength() > annotationEnd + 1 && document.get(annotationEnd, 2).equals(document.getDefaultLineDelimiter())) {
400 length += 2;
401 }
402 } catch (BadLocationException e1) {
403 // TODO Auto-generated catch block
404 e1.printStackTrace();
405 }
406
407 try {
408 document.replace(offset, length, "");
409 } catch (BadLocationException e) {
410 // TODO Auto-generated catch block
411 e.printStackTrace();
412 }
413 }
414 }
415 }