View Javadoc

1   /*
2    * Created on May 18, 2005. Copyright 1999-2006 Faculty of Mathematics, Physics
3    * and Informatics, Comenius University, Bratislava. This file is protected by
4    * the Mozilla Public License version 1.1 (the "License"); you may not use this
5    * file except in compliance with the License. You may obtain a copy of the
6    * License at http://euromath2.sourceforge.net/license.html Unless required by
7    * applicable law or agreed to in writing, software distributed under the
8    * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS
9    * OF ANY KIND, either express or implied. See the License for the specific
10   * language governing permissions and limitations under the License.
11   */
12  package sk.uniba.euromath.document;
13  
14  import java.util.ArrayList;
15  import java.util.Collection;
16  import java.util.Collections;
17  import java.util.Comparator;
18  import java.util.HashSet;
19  import java.util.List;
20  import java.util.Set;
21  import java.util.TreeSet;
22  
23  import javax.xml.namespace.QName;
24  
25  import org.apache.commons.lang.NullArgumentException;
26  import org.apache.commons.lang.StringUtils;
27  import org.eclipse.jface.dialogs.IInputValidator;
28  import org.eclipse.jface.dialogs.InputDialog;
29  import org.eclipse.jface.dialogs.MessageDialog;
30  import org.eclipse.jface.window.Window;
31  import org.eclipse.swt.widgets.Display;
32  import org.eclipse.swt.widgets.Shell;
33  import org.w3c.dom.Attr;
34  import org.w3c.dom.Comment;
35  import org.w3c.dom.DocumentFragment;
36  import org.w3c.dom.Element;
37  import org.w3c.dom.Entity;
38  import org.w3c.dom.EntityReference;
39  import org.w3c.dom.Node;
40  import org.w3c.dom.ProcessingInstruction;
41  import org.w3c.dom.traversal.NodeFilter;
42  
43  import sk.baka.ikslibs.DOMUtils;
44  import sk.baka.ikslibs.interval.DOMInterval;
45  import sk.baka.ikslibs.modify.DOMMutils;
46  import sk.baka.ikslibs.ptr.DOMPoint;
47  import sk.baka.ikslibs.ptr.DomPointer;
48  import sk.baka.ikslibs.ptr.DomPointerFactory;
49  import sk.baka.xml.gene.ExportException;
50  import sk.baka.xml.schematic.rules.AttributeRule;
51  import sk.baka.xml.schematic.rules.ElementRule;
52  import sk.baka.xml.schematic.rules.INameList;
53  import sk.baka.xml.schematic.rules.IValueRule;
54  import sk.baka.xml.schematic.rules.InsertList;
55  import sk.baka.xml.schematic.rules.NewElementRule;
56  import sk.uniba.euromath.document.schema.SchematicUtils;
57  import sk.uniba.euromath.editor.dialogs.Dialogs;
58  import sk.uniba.euromath.editor.dialogs.ProcessingInstructionDialog;
59  import sk.uniba.euromath.editor.dialogs.WidgetWrapperDialog;
60  import sk.uniba.euromath.editor.lang.Messages;
61  import sk.uniba.euromath.editor.widgets.EntityList;
62  import sk.uniba.euromath.editor.widgets.namelist.DisplayableNameItemTypeEnum;
63  import sk.uniba.euromath.editor.widgets.namelist.DisplayableNameList;
64  import sk.uniba.euromath.editor.widgets.namelist.DisplayableNameListImpl;
65  import sk.uniba.euromath.editor.widgets.namelist.NameListItemChooser;
66  
67  /***
68   * Provides basic editing features, document modification driven by schema.
69   * 
70   * @author Martin Vysny
71   * @author Tomáš Studva
72   * @TODO MOTO> Move to .editor package.
73   */
74  public final class DocumentModifyHelper {
75          /***
76           * Constuctor.
77           * 
78           * @param xmlAccess
79           *                the document instance.
80           */
81          public DocumentModifyHelper(XMLAccess xmlAccess) {
82                  super();
83                  this.xmlAccess = xmlAccess;
84          }
85  
86          /***
87           * The document instance.
88           */
89          private final XMLAccess xmlAccess;
90  
91          /***
92           * Manages the process of inserting a new attribute into given element
93           * by wizard.
94           * 
95           * @param parent
96           *                parent window. Should not be <code>null</code>.
97           * @param e
98           *                the element where to insert new attribute.
99           * @throws ExportException
100          *                 when document modification finalization fails.
101          */
102         public void insertNewAttribute(Shell parent, Element e)
103                         throws ExportException {
104                 INameList<AttributeRule> list = xmlAccess.getSchema()
105                                 .getElementRule(e).getInsertableAttributes();
106                 if (list.getForeignNames().size() + list.getLocalNames().size() == 0) {
107                         MessageDialog
108                                         .openInformation(
109                                                         parent,
110                                                         Messages
111                                                                         .getString("INFORMATION"), Messages.getString("NO_INSERTABLE_ATTRIBUTE")); //$NON-NLS-1$ //$NON-NLS-2$
112                         return;
113                 }
114                 WidgetWrapperDialog<NameListItemChooser<AttributeRule>> dlg = Dialogs
115                                 .createAttributeCreator(parent, list,
116                                                 xmlAccess, xmlAccess
117                                                                 .getNsManager());
118                 if (dlg.open() != Window.OK)
119                         return;
120                 // create an attribute
121                 xmlAccess.getUndoManager().mark();
122                 xmlAccess.getModifier().startModify();
123                 xmlAccess.getModifier().createAttribute(e,
124                                 dlg.getWidget().getSelectedQName(),
125                                 dlg.getWidget().getSelectedValue());
126                 xmlAccess.getModifier().endModify();
127         }
128 
129         /***
130          * Manages the process of modifying the attribute value by wizard.
131          * 
132          * @param parent
133          *                parent window. Should not be <code>null</code>.
134          * @param attr
135          *                the attribute whose value has to be modified.
136          * @throws ExportException
137          *                 when document modification finalization fails.
138          */
139         public void modifyAttribute(Shell parent, Attr attr)
140                         throws ExportException {
141                 AttributeRule rule = xmlAccess.getSchema().getElementRule(
142                                 attr.getOwnerElement()).getAttributeRule(attr);
143                 WidgetWrapperDialog<NameListItemChooser<AttributeRule>> dlg = Dialogs
144                                 .createAttributeEditor(parent, attr, rule,
145                                                 xmlAccess, xmlAccess
146                                                                 .getNsManager());
147                 if (dlg.open() != Window.OK)
148                         return;
149                 // modify the attribute value
150                 xmlAccess.getUndoManager().mark();
151                 xmlAccess.getModifier().startModify();
152                 xmlAccess.getModifier().setText(attr,
153                                 dlg.getWidget().getSelectedValue());
154                 xmlAccess.getModifier().endModify();
155         }
156 
157         /***
158          * Tests if given attribute can be deleted.
159          * 
160          * @param tested
161          *                attribute
162          * @return null if can be deleted, otherwise error message
163          */
164         public String canDeleteAttribute(Attr attr) {
165                 String result = checkInEntity(attr);
166                 if (result != null)
167                         return result;
168 
169                 return xmlAccess.getSchema().getElementRule(
170                                 attr.getOwnerElement()).isDeletableAttribute(
171                                 attr);
172         }
173 
174         /***
175          * Manages the process of deleting the attribute.
176          * 
177          * @param parent
178          *                parent window. Should not be <code>null</code>.
179          * @param attr
180          *                the attribute which has to be deleted.
181          * @throws ExportException
182          *                 when document modification finalization fails.
183          */
184         public void deleteAttribute(Shell parent, Attr attr)
185                         throws ExportException {
186                 final String errorMsg = canDeleteAttribute(attr);
187                 if (errorMsg != null)
188                         MessageDialog
189                                         .openError(
190                                                         parent,
191                                                         Messages
192                                                                         .getString("ERROR"), //$NON-NLS-1$
193                                                         Messages
194                                                                         .getString(
195                                                                                         "ATTRIBUTE_CANNOT_BE_REMOVED", DOMUtils //$NON-NLS-1$
196                                                                                                         .printQName(attr))
197                                                                         + "\n" + errorMsg); //$NON-NLS-1$
198                 // attribute is deletable
199                 final boolean result = MessageDialog
200                                 .openQuestion(
201                                                 parent,
202                                                 Messages.getString("QUESTION"), Messages.getString("REMOVE_ATTRIBUTE_QUESTION", DOMUtils.printQName(attr))); //$NON-NLS-1$ //$NON-NLS-2$
203                 // if user does not want to remove the attribute, bail out
204                 if (!result)
205                         return;
206                 // create an attribute
207                 xmlAccess.getUndoManager().mark();
208                 xmlAccess.getModifier().startModify();
209                 xmlAccess.getModifier().remove(attr);
210                 xmlAccess.getModifier().endModify();
211         }
212 
213         /***
214          * Tests if elements can bedelted
215          * 
216          * @param elements
217          *                to test
218          * @param parent -
219          *                output argument, method computes common parent of
220          *                elements and stores it in parent
221          * @return null if can be deleted, otherwise error message
222          */
223         public String canDeleteElements(Set<Element> elements, Node parent) {
224                 if (elements.size() == 0)
225                         throw new IllegalArgumentException(
226                                         "The set of elements must not be empty."); //$NON-NLS-1$
227                 // get the parent of all elements
228                 for (Element e : elements) {
229                         if (parent == null)
230                                 parent = e.getParentNode();
231                         else {
232                                 if (!parent.isSameNode(e.getParentNode()))
233                                         throw new IllegalArgumentException(
234                                                         "The elements have no common parent."); //$NON-NLS-1$
235                         }
236                 }
237                 if (parent.getNodeType() != Node.ELEMENT_NODE) {
238                         return Messages.getString("CANNOT_DELETE_ROOT"); //$NON-NLS-1$
239                 }
240                 return null;
241         }
242 
243         /***
244          * Manages the process of deleting the elements. All elements must have
245          * the same parent.
246          * 
247          * @param shell
248          *                parent window. Should not be <code>null</code>.
249          * @param elements
250          *                set of <code>Element</code> s that must be deleted.
251          * @throws ExportException
252          *                 when document modification finalization fails.
253          */
254         public void deleteElements(Shell shell, Set<Element> elements)
255                         throws ExportException {
256                 Node parent = null;
257                 final String errorMsg = canDeleteElements(elements, parent);
258                 if (errorMsg != null)
259                         MessageDialog.openError(shell, Messages
260                                         .getString("ERROR"), //$NON-NLS-1$
261                                         errorMsg); //$NON-NLS-1$
262 
263                 ElementRule rule = xmlAccess.getSchema().getElementRule(
264                                 (Element) parent);
265                 DeleteElementsManager dem = new DeleteElementsManager();
266                 if (!dem.execute(shell, elements, DOMUtils.printQName(parent),
267                                 rule))
268                         return;
269                 // delete the elements
270                 if (dem.result == null) {
271                         Set<Element> delete = new HashSet<Element>();
272                         delete.add((Element) parent);
273                         deleteElements(shell, delete);
274                         return;
275                 }
276                 if (dem.result.size() != 0) {
277                         Set<Element> delete = new HashSet<Element>(elements);
278                         delete.addAll(dem.result);
279                         elements = delete;
280                 }
281                 // announce the start of modification
282                 xmlAccess.getUndoManager().mark();
283                 xmlAccess.getModifier().startModify();
284                 // delete the elements
285                 for (Element e : elements) {
286                         xmlAccess.getModifier().remove(e);
287                         xmlAccess.getIDManager().removeTree(e);
288                 }
289                 xmlAccess.getModifier().endModify();
290         }
291 
292         /***
293          * Manages the process of element deletion.
294          * 
295          * @author Martin Vysny
296          */
297         private static class DeleteElementsManager {
298                 /***
299                  * List of elements that must be deleted aswell. If the set is
300                  * empty then no other elements must be deleted. If the set is
301                  * <code>null</code> then the parent element must be deleted.
302                  * Valid after the <code>execute()</code> function returns
303                  * <code>true</code>.
304                  */
305                 List<? extends Element> result = null;
306 
307                 /***
308                  * Drives the user through the process.
309                  * 
310                  * @param parent
311                  *                parent window.
312                  * @param elements
313                  *                the list of elements that have to be deleted.
314                  * @param parentName
315                  *                the name of the parent element. Used only to
316                  *                display the name on the shell.
317                  * @param parentRule
318                  *                rule for parent element.
319                  * @return true if user permitted the deletion, false if the
320                  *         request was rejected or an error occured.
321                  */
322                 boolean execute(Shell parent, Set<Element> elements,
323                                 String parentName, ElementRule parentRule) {
324                         result = parentRule.areElementsDeletable(elements);
325                         boolean dlgResult;
326                         if (result == null) {
327                                 dlgResult = MessageDialog
328                                                 .openQuestion(
329                                                                 parent,
330                                                                 Messages
331                                                                                 .getString("QUESTION"), //$NON-NLS-1$
332                                                                 Messages
333                                                                                 .getString(
334                                                                                                 "REQ_PARENT_ELEMENT_DEL", parentName)); //$NON-NLS-1$
335                         } else if (result.size() == 0) {
336                                 dlgResult = MessageDialog
337                                                 .openQuestion(
338                                                                 parent,
339                                                                 Messages
340                                                                                 .getString("QUESTION"), //$NON-NLS-1$
341                                                                 Messages
342                                                                                 .getString(
343                                                                                                 "ELEMENT_DELETE_QUERY", printList(elements))); //$NON-NLS-1$
344                         } else {
345                                 dlgResult = MessageDialog
346                                                 .openQuestion(
347                                                                 parent,
348                                                                 Messages
349                                                                                 .getString("QUESTION"), //$NON-NLS-1$
350                                                                 Messages
351                                                                                 .getString(
352                                                                                                 "REQ_ELEMENTS_DEL", printList(result))); //$NON-NLS-1$
353                         }
354                         return dlgResult;
355                 }
356 
357                 /***
358                  * Prints given elements as a comma-separated list.
359                  * 
360                  * @param elements
361                  *                the elements, whose names to print.
362                  * @return comma separated element names.
363                  */
364                 private static String printList(
365                                 Collection<? extends Element> elements) {
366                         StringBuilder builder = new StringBuilder();
367                         for (Element e : elements) {
368                                 if (builder.length() != 0)
369                                         builder.append(", "); //$NON-NLS-1$
370                                 builder.append(DOMUtils.printQName(e));
371                         }
372                         return builder.toString();
373                 }
374         }
375 
376         /***
377          * Tests if nodes can be enclosed.
378          * 
379          * @param start
380          *                start of the interval. Must point before the
381          *                <code>end</code> parameter.
382          * @param end
383          *                end of the interval.
384          * @param outElements -
385          *                output argument, method computes elements that are
386          *                allowed by schema to enclose interval
387          * @return null if can be enclosed, otherwise error message
388          */
389         public String canEncloseNodes(DomPointer start, DomPointer end,
390                         INameList<NewElementRule> outElements) {
391                 if (!start.parent.isSameNode(end.parent))
392                         throw new IllegalArgumentException(
393                                         "The pointers must point into one node."); //$NON-NLS-1$
394                 if (start.compareTo(end) >= 0)
395                         throw new IllegalArgumentException(
396                                         "The end pointer must point after the start pointer."); //$NON-NLS-1$
397                 if (start.inEntity()) {
398                         // cannot modify entity
399                         return Messages.getString("CANNOT_MODIFY_ENTITY"); //$NON-NLS-1$
400                 }
401                 final ElementRule rule = xmlAccess.getSchema().getElementRule(
402                                 (Element) start.parent);
403                 outElements = rule.getEnclosingElements(start.ip, end.ip);
404 
405                 if (outElements == null) {
406                         // cannot enclose
407                         return Messages.getString("ENCLOSE_FORBIDDEN"); //$NON-NLS-1$
408                 }
409                 return null;
410         }
411 
412         /***
413          * Manages the process of enclosing the nodes. All nodes must have the
414          * same parent. Both pointers may point inside a text.
415          * 
416          * @param start
417          *                start of the interval. Must point before the
418          *                <code>end</code> parameter.
419          * @param end
420          *                end of the interval.
421          * @param parent
422          *                parent window. Should not be <code>null</code>.
423          * @throws ExportException
424          *                 when document modification finalization fails.
425          */
426         public void encloseNodes(Shell parent, DomPointer start, DomPointer end)
427                         throws ExportException {
428                 final INameList<NewElementRule> elements = null;
429                 final String errorMsg = canEncloseNodes(start, end, elements);
430 
431                 if (errorMsg != null) {
432                         // cannot enclose, quit
433                         MessageDialog.openInformation(parent, Messages
434                                         .getString("ERROR"), //$NON-NLS-1$
435                                         errorMsg); //$NON-NLS-1$
436                         return;
437                 }
438                 final DisplayableNameList<NewElementRule> dnl = new DisplayableNameListImpl<NewElementRule>(
439                                 elements, xmlAccess, xmlAccess.getNsManager(),
440                                 true);
441                 final WidgetWrapperDialog<NameListItemChooser<NewElementRule>> dlg = Dialogs
442                                 .createElementCreator(
443                                                 parent,
444                                                 xmlAccess,
445                                                 Messages
446                                                                 .getString("ENCLOSE_NODES"), dnl); //$NON-NLS-1$
447                 if (dlg.open() != Window.OK)
448                         return;
449                 // enclose the nodes with selected element.
450                 xmlAccess.getUndoManager().mark();
451                 xmlAccess.getModifier().startModify();
452                 final QName qname = dlg.getWidget().getSelectedQName();
453                 Element e = xmlAccess.getDocument().createElementNS(
454                                 qname.getNamespaceURI(),
455                                 DOMUtils.printQName(qname));
456                 // this node is the last node that will be moved into enclosing
457                 // element
458                 Node last;
459                 // split the nodes and update the pointers if necessary
460                 if (end.ip.pos != 0) {
461                         last = DOMMutils.splitText(end);
462                 } else
463                         last = end.pointsTo;
464                 // this splits the node pointed to by 'start' if neccessary.
465                 DOMMutils.insertNode(e, start, false);
466                 Node first = e.getNextSibling();
467                 // move all nodes, starting with first to e. 'last' must not be
468                 // moved.
469                 while (first != last) {
470                         Node next = first.getNextSibling();
471                         DOMMutils.remove(first);
472                         DOMMutils.insertNode(first, DomPointerFactory.create(e,
473                                         (Node) null), false);
474                         first = next;
475                 }
476                 xmlAccess.getModifier().endModify();
477         }
478 
479         /***
480          * Test if element can be declosed.
481          * 
482          * @param e
483          *                element to declose
484          * @return null if can be declosed, otherwise error message
485          */
486         public String canDecloseNodes(Element e) {
487                 if ((e.getParentNode() == null)
488                                 || (e.getParentNode().getNodeType() != Node.ELEMENT_NODE)) {
489                         // cannot declose root node
490                         return "Cannot declose root node.";
491                 }
492                 final ElementRule rule = xmlAccess.getSchema().getElementRule(
493                                 (Element) e.getParentNode());
494                 return rule.isDeclosable(e);
495         }
496 
497         /***
498          * Manages the process of declosing the nodes - replaces given element
499          * with its contents.
500          * 
501          * @param parent
502          *                parent window. Should not be <code>null</code>.
503          * @param e
504          *                the element, that shall be replaced by its children if
505          *                the declosing is allowed.
506          * @throws ExportException
507          *                 when document modification finalization fails.
508          */
509         public void decloseNodes(Shell parent, Element e)
510                         throws ExportException {
511                 final String errorMsg = canDecloseNodes(e);
512                 if (errorMsg != null)
513                         MessageDialog
514                                         .openInformation(
515                                                         parent,
516                                                         Messages
517                                                                         .getString("INFORMATION"), //$NON-NLS-1$
518                                                         Messages
519                                                                         .getString("DECLOSE_FORBIDDEN") + //$NON-NLS-1$
520                                                                         "\n"
521                                                                         + errorMsg); //$NON-NLS-1$
522                 final boolean result = MessageDialog
523                                 .openQuestion(parent,
524                                                 "Question", //$NON-NLS-1$
525                                                 Messages
526                                                                 .getString(
527                                                                                 "DECLOSE_QUERY", DOMUtils.printQName(e))); //$NON-NLS-1$
528                 if (!result)
529                         return;
530                 // carry the declose operation
531                 xmlAccess.getUndoManager().mark();
532                 xmlAccess.getModifier().startModify();
533                 Node node = e.getFirstChild();
534                 Node insertAfter = e;
535                 while (node != null) {
536                         Node next = node.getNextSibling();
537                         xmlAccess.getModifier().remove(node);
538                         DomPointer ptr = DomPointerFactory.create(insertAfter)
539                                         .getNextSibling();
540                         // if next node is null then merge current node - it may
541                         // be a text
542                         // node thus it may be mergeable with its new sibling.
543                         DOMMutils.insertNode(node, ptr, next == null);
544                         insertAfter = node;
545                         node = next;
546                 }
547                 // this will automatically merge first node if needed.
548                 xmlAccess.getModifier().remove(e);
549                 xmlAccess.getModifier().endModify();
550         }
551 
552         /***
553          * Tests if processing instruction can be inserted.
554          * 
555          * @param pointer
556          * @return null if can be inserted, otherwise error message
557          */
558         public String canInsertProcessingInstruction(DomPointer pointer) {
559                 return checkInEntity(pointer);
560         }
561 
562         /***
563          * Inserts processing instruction at pointer.
564          * 
565          * @param shell
566          *                parent window. !!! Cannot be <code>null</code>.
567          * @param pointer
568          *                dom pointer where to insert
569          * @throws ExportException
570          */
571         public void insertProcessingInstruction(Shell shell, DomPointer pointer)
572                         throws ExportException {
573                 final String errorMsg = canInsertProcessingInstruction(pointer);
574                 if (errorMsg != null)
575                         MessageDialog.openInformation(shell, Messages
576                                         .getString("ERROR"), //$NON-NLS-1$
577                                         errorMsg); //$NON-NLS-1$
578                 ProcessingInstructionDialog dialog = new ProcessingInstructionDialog(
579                                 shell,
580                                 Messages
581                                                 .getString("INSERT_PROCESSING_INSTRUCTION"), //$NON-NLS-1$
582                                 null, null);
583 
584                 if (dialog.open() == Window.OK) {
585                         String target = dialog.getTarget().trim();
586                         String data = dialog.getData().trim();
587                         this.xmlAccess.getModifier().startModify();
588                         this.xmlAccess.getUndoManager().mark();
589                         DOMMutils.insertNode(this.xmlAccess.getDocument()
590                                         .createProcessingInstruction(target,
591                                                         data), pointer, false);
592                         this.xmlAccess.getModifier().endModify();
593                 }
594         }
595 
596         /***
597          * Modifies processing instruction node by dialog.
598          * 
599          * @param shell
600          *                parent window. !!! Cannot be <code>null</code>.
601          * @param node
602          *                processing instruction node to modify
603          * @throws ExportException
604          */
605         public void modifyProcessingInstruction(Shell shell,
606                         ProcessingInstruction pi) throws ExportException {
607                 ProcessingInstructionDialog dialog = new ProcessingInstructionDialog(
608                                 shell,
609                                 Messages
610                                                 .getString("INSERT_PROCESSING_INSTRUCTION"), //$NON-NLS-1$
611                                 pi.getTarget(), pi.getData());
612 
613                 if (dialog.open() == Window.OK) {
614                         String target = dialog.getTarget().trim();
615                         String data = dialog.getData().trim();
616                         this.xmlAccess.getModifier().startModify();
617                         this.xmlAccess.getUndoManager().mark();
618                         DomPointer pointer = DomPointerFactory.create(pi);
619                         DOMMutils.insertNode(this.xmlAccess.getDocument()
620                                         .createProcessingInstruction(target,
621                                                         data), pointer, false);
622                         this.xmlAccess.getModifier().remove(pi);
623                         this.xmlAccess.getModifier().endModify();
624                 }
625         }
626 
627         /***
628          * Tests if comment can be inserted.
629          * 
630          * @param pointer
631          * @return null if can be inserted, otherwise error message
632          */
633         public String canInsertComment(DomPointer pointer) {
634                 return checkInEntity(pointer);
635         }
636 
637         /***
638          * Inserts processing instruction at pointer.
639          * 
640          * @param shell
641          *                parent window. !!! Cannot be <code>null</code>.
642          * @param pointer
643          *                dom pointer where to insert
644          * @throws ExportException
645          */
646         public void insertComment(Shell shell, DomPointer pointer)
647                         throws ExportException {
648                 final String errorMsg = canInsertComment(pointer);
649                 if (errorMsg != null)
650                         MessageDialog.openInformation(shell, Messages
651                                         .getString("ERROR"), //$NON-NLS-1$
652                                         errorMsg); //$NON-NLS-1$
653                 InputDialog dialog = new InputDialog(
654                                 shell,
655                                 Messages.getString("INSERT_COMMENT"), //$NON-NLS-1$
656                                 Messages.getString("INSERT_COMMENT_ENTER_TEXT"), "", null); //$NON-NLS-1$ //$NON-NLS-2$
657 
658                 if (dialog.open() == Window.OK) {
659                         String inputStr = dialog.getValue().trim();
660                         insertComment(inputStr, shell, pointer);
661                 }
662         }
663 
664         /***
665          * Inserts comment with text text at pointer.
666          * 
667          * @param text
668          *                text of comment to insert
669          * @param shell
670          *                parent window. !!! Cannot be <code>null</code>.
671          * @param pointer
672          *                dom pointer where to insert
673          * @throws ExportException
674          */
675         public void insertComment(String text, Shell shell, DomPointer pointer)
676                         throws ExportException {
677                 assert (text != null);
678                 this.xmlAccess.getModifier().startModify();
679                 this.xmlAccess.getUndoManager().mark();
680                 DOMMutils.insertNode(this.xmlAccess.getDocument()
681                                 .createComment(text), pointer, false);
682                 this.xmlAccess.getModifier().endModify();
683         }
684 
685         /***
686          * Modifies comment node by dialog.
687          * 
688          * @param shell
689          *                parent window. !!! Cannot be <code>null</code>.
690          * @param node
691          *                comment node to modify
692          * @throws ExportException
693          */
694         public void modifyComment(Shell shell, Comment comment)
695                         throws ExportException {
696                 InputDialog dialog = new InputDialog(shell,
697                                 "Modify comment node", "Modify comment node",
698                                 comment.getNodeValue(), null);
699                 if (dialog.open() == Window.OK) {
700                         String inputStr = dialog.getValue().trim();
701                         this.xmlAccess.getUndoManager().mark();
702                         this.xmlAccess.getModifier().startModify();
703                         DOMMutils.setText(comment, inputStr);
704                         this.xmlAccess.getModifier().endModify();
705                 }
706         }
707 
708         /***
709          * Manages the process of inserting an entity into selected position by
710          * dialog.
711          * 
712          * @param ptr
713          *                the desired place.
714          * @param parent
715          *                parent window. Should not be <code>null</code>.
716          * @throws ExportException
717          *                 when document modification finalization fails.
718          */
719         public void insertEntity(Shell parent, DomPointer ptr)
720                         throws ExportException {
721                 // get valid entities
722                 List<String> validEntities = getInsertableEntities(parent, ptr);
723                 if (validEntities == null)
724                         return;
725 
726                 WidgetWrapperDialog<EntityList> dlg = Dialogs
727                                 .createEntityLister(
728                                                 parent,
729                                                 xmlAccess,
730                                                 Messages
731                                                                 .getString("CREATE_ENTITY"), validEntities); //$NON-NLS-1$
732                 if (dlg.open() != Window.OK)
733                         return;
734                 final String entityName = dlg.getWidget().getEntityName();
735                 // ok, insert the entity at specified place
736                 insertEntity(entityName, ptr);
737         }
738 
739         /***
740          * Inserts entity to given position.
741          * 
742          * @param entityName
743          *                the entity name.
744          * @param ptr
745          *                the insert point.
746          * @throws ExportException
747          *                 if document modification finalization fails.
748          */
749         public void insertEntity(String entityName, DomPointer ptr)
750                         throws ExportException {
751                 xmlAccess.getUndoManager().mark();
752                 xmlAccess.getModifier().startModify();
753                 EntityReference er = xmlAccess.getDocument()
754                                 .createEntityReference(entityName);
755                 DOMMutils.insertNode(er, ptr, false);
756                 xmlAccess.getModifier().endModify();
757         }
758 
759         /***
760          * Computes insertable entities. Shows various warning dialogs if no
761          * entities can be found etc.
762          * 
763          * @param ptr
764          *                the desired place.
765          * @param parent
766          *                parent window. Can be <code>null</code>.
767          * @return entity names that are insertable at given pointer. May return
768          *         <code>null</code> if no entities are suitable.
769          */
770         public List<String> getInsertableEntities(final Shell parent,
771                         final DomPointer ptr) {
772                 if (ptr == null)
773                         throw new NullArgumentException("ptr"); //$NON-NLS-1$
774                 if (checkInEntity(parent, ptr))
775                         return null;
776                 final ElementRule rule = xmlAccess.getSchema().getElementRule(
777                                 ptr.parentElement);
778                 // get all entity names
779                 Set<String> entities = xmlAccess.getEntityManager()
780                                 .getEntityNames();
781                 if (entities.isEmpty()) {
782                         // no entities available, quit
783                         if (parent != null)
784                                 MessageDialog
785                                                 .openInformation(
786                                                                 parent,
787                                                                 Messages
788                                                                                 .getString("INFORMATION"), Messages.getString("NO_KNOWN_ENTITIES")); //$NON-NLS-1$ //$NON-NLS-2$
789                         return null;
790                 }
791                 // compute list of insertable entities
792                 final List<String> validEntities = new ArrayList<String>(
793                                 entities.size());
794                 for (final String entityName : entities) {
795                         final Entity entity = xmlAccess.getEntityManager()
796                                         .getEntity(entityName);
797                         if (rule.isInsertable(ptr.ip, entity) == null)
798                                 validEntities.add(entityName);
799                 }
800                 if (validEntities.isEmpty()) {
801                         // no entities available, quit
802                         if (parent != null)
803                                 MessageDialog
804                                                 .openInformation(
805                                                                 parent,
806                                                                 Messages
807                                                                                 .getString("INFORMATION"), Messages.getString("NO_INSERTABLE_ENTITY")); //$NON-NLS-1$ //$NON-NLS-2$
808                         return null;
809                 }
810                 return validEntities;
811         }
812 
813         /***
814          * Computes list of names of elements, insertable at given pointer.
815          * 
816          * @param pointer
817          * @return ordered list of qnames.
818          */
819         public List<QName> getInsertableElementsQNames(final DomPointer pointer) {
820                 if (pointer == null)
821                         throw new IllegalArgumentException("pointer is null."); //$NON-NLS-1$
822                 if (pointer.parentElement == null)
823                         throw new IllegalArgumentException(
824                                         "Cannot insert elements before/after root element."); //$NON-NLS-1$
825                 final List<InsertList> insertLists = xmlAccess.getSchema()
826                                 .getElementRule(pointer.parentElement)
827                                 .getInsertableElements(pointer.ip);
828                 final String localNamespace = pointer.parentElement
829                                 .getNamespaceURI();
830                 // maintain an ordered set of qnames found so far.
831                 final TreeSet<QName> result = new TreeSet<QName>(
832                                 new Comparator<QName>() {
833                                         public int compare(QName o1, QName o2) {
834                                                 if (DOMUtils
835                                                                 .equalsURI(
836                                                                                 o1
837                                                                                                 .getNamespaceURI(),
838                                                                                 localNamespace)) {
839                                                         if (!DOMUtils
840                                                                         .equalsURI(
841                                                                                         o2
842                                                                                                         .getNamespaceURI(),
843                                                                                         localNamespace))
844                                                                 return -1;
845                                                         return o1
846                                                                         .getLocalPart()
847                                                                         .compareTo(
848                                                                                         o2
849                                                                                                         .getLocalPart());
850                                                 }
851                                                 if (DOMUtils
852                                                                 .equalsURI(
853                                                                                 o2
854                                                                                                 .getNamespaceURI(),
855                                                                                 localNamespace))
856                                                         return 1;
857                                                 // ok, both o1 and o2 are
858                                                 // non-local
859                                                 final int result = StringUtils
860                                                                 .defaultString(
861                                                                                 o1
862                                                                                                 .getPrefix())
863                                                                 .compareTo(
864                                                                                 StringUtils
865                                                                                                 .defaultString(o2
866                                                                                                                 .getPrefix()));
867                                                 if (result != 0)
868                                                         return result;
869                                                 return o1
870                                                                 .getLocalPart()
871                                                                 .compareTo(
872                                                                                 o2
873                                                                                                 .getLocalPart());
874                                         }
875                                 });
876                 for (final InsertList insertList : insertLists) {
877                         final INameList<NewElementRule> nl = insertList
878                                         .getFirstNamelistAt(pointer.ip);
879                         final DisplayableNameList<NewElementRule> dnl = new DisplayableNameListImpl<NewElementRule>(
880                                         nl, xmlAccess,
881                                         xmlAccess.getNsManager(), true);
882                         final List<QName> qnames = getDomQnames(dnl);
883                         result.addAll(qnames);
884                 }
885                 return new ArrayList<QName>(result);
886         }
887 
888         /***
889          * Computes QNames of elements from DisplayableNameList.
890          * 
891          * @param dnl
892          *                the list. If <code>null</code> then empty list is
893          *                returned.
894          * @return list of qnames with
895          *         {@link NamespaceManager#getBestPrefix(String) best prefixes}.
896          */
897         public List<QName> getDomQnames(DisplayableNameList dnl) {
898                 List<QName> result = new ArrayList<QName>();
899                 if (dnl != null) {
900                         for (int i = 0; i < dnl.getLength(); i++) {
901                                 // ak je to namespace
902                                 if (dnl.getType(i) == DisplayableNameItemTypeEnum.ITEM_NAMESPACE)
903                                         continue;
904                                 final String ns = dnl.getDomNamespaceUri(i);
905                                 final String prefix = xmlAccess.getNsManager()
906                                                 .getBestPrefix(ns);
907                                 result.add(dnl.getDomQName(i, prefix));
908                         }
909                 }
910                 return result;
911         }
912 
913         /***
914          * Inserts text to given point. Displays a dialog allowing user to enter
915          * text being inserted. Only valid text is allowed.
916          * 
917          * @param point
918          *                insert the text here.
919          * @param shell
920          *                parent window. !!! Cannot be <code>null</code>.
921          * @param nodeType
922          *                the type of node to insert. May be
923          *                {@link Node#CDATA_SECTION_NODE} or
924          *                {@link Node#TEXT_NODE} only.
925          * @throws ExportException
926          */
927         public void insertTextNode(final Shell shell, final DomPointer point,
928                         final short nodeType) throws ExportException {
929                 if (!DOMUtils.isText(nodeType))
930                         throw new IllegalArgumentException(
931                                         "nodeType is not text"); //$NON-NLS-1$
932                 if (point.inEntity()) {
933                         MessageDialog
934                                         .openInformation(
935                                                         shell,
936                                                         Messages
937                                                                         .getString("ERROR"), //$NON-NLS-1$
938                                                         Messages
939                                                                         .getString("CANNOT_MODIFY_ENTITY")); //$NON-NLS-1$
940                         return;
941                 }
942                 // create the input validator.
943                 final IInputValidator validator = SchematicUtils
944                                 .createTextValidator(xmlAccess.getSchema(),
945                                                 new DOMInterval(point, point));
946                 final String text = (nodeType == Node.TEXT_NODE) ? Messages
947                                 .getString("INSERT_TEXT_NODE_CONTENTS") //$NON-NLS-1$
948                                 : Messages
949                                                 .getString("INSERT_CDATA_NODE_CONTENTS"); //$NON-NLS-1$
950                 final InputDialog dialog = new InputDialog(shell, Messages
951                                 .getString("INSERT_TEXT_NODE"), //$NON-NLS-1$
952                                 text, "", validator); //$NON-NLS-1$
953                 if (dialog.open() == Window.OK) {
954                         final String inputStr = dialog.getValue().trim();
955                         final DocumentModifier dm = xmlAccess.getModifier();
956                         xmlAccess.getUndoManager().mark();
957                         dm.startModify();
958                         DOMMutils.insertText(point, inputStr, nodeType);
959                         dm.endModify();
960                 }
961         }
962 
963         /***
964          * Modifies text of node by dialog.
965          * 
966          * @param node
967          *                must be text node
968          * @param shell
969          *                parent window. !!!Cannot be <code>null</code>.
970          * @throws ExportException
971          *                 if document fails to transform.
972          */
973         public void modifyTextNode(Shell shell, Node node)
974                         throws ExportException {
975                 if (shell == null)
976                         throw new IllegalArgumentException(
977                                         "Shell cannot be null."); //$NON-NLS-1$
978                 if (!DOMUtils.isText(node))
979                         throw new IllegalArgumentException(
980                                         "The node must be a text/cdata node"); //$NON-NLS-1$
981                 // create the input validator.
982                 final DomPointer ptr = DomPointerFactory.create(node);
983                 final IInputValidator validator = SchematicUtils
984                                 .createTextValidator(
985                                                 xmlAccess.getSchema(),
986                                                 new DOMInterval(
987                                                                 ptr,
988                                                                 ptr
989                                                                                 .getNextSibling()));
990                 final InputDialog dialog = new InputDialog(
991                                 Display.getCurrent().getActiveShell(),
992                                 Messages.getString("MODIFY_TEXT_NODE"), Messages.getString("MODIFY_TEXT"), node //$NON-NLS-1$ //$NON-NLS-2$
993                                                 .getNodeValue(), validator);
994                 if (dialog.open() == Window.OK) {
995                         final DocumentModifier dm = xmlAccess.getModifier();
996                         dm.startModify();
997                         xmlAccess.getUndoManager().mark();
998                         dm.setText(node, dialog.getValue());
999                         xmlAccess.getUndoManager().mark();
1000                         dm.endModify();
1001                 }
1002         }
1003 
1004         /***
1005          * Checks if given node is in entity. If yes then error dialog is shown.
1006          * 
1007          * @param shell
1008          *                parent, should not be <code>null</code>.
1009          * @param node
1010          *                node to check
1011          * @return <code>true</code> if node is in entity, <code>false</code>
1012          *         otherwise.
1013          */
1014         public boolean checkInEntity(final Shell shell, final Node node) {
1015                 if (DOMUtils.isInEntity(node)) {
1016                         MessageDialog
1017                                         .openInformation(
1018                                                         shell,
1019                                                         Messages
1020                                                                         .getString("ERROR"), //$NON-NLS-1$
1021                                                         Messages
1022                                                                         .getString("CANNOT_MODIFY_ENTITY")); //$NON-NLS-1$
1023                         return true;
1024                 }
1025                 return false;
1026         }
1027 
1028         /***
1029          * Checks if given pointer points into entity. If yes then error dialog
1030          * is shown.
1031          * 
1032          * @param shell
1033          *                parent, should not be <code>null</code>.
1034          * @param ptr
1035          *                the pointer to check
1036          * @return <code>true</code> if the pointer points into an entity,
1037          *         <code>false</code> otherwise.
1038          */
1039         public boolean checkInEntity(final Shell shell, final DomPointer ptr) {
1040                 if (ptr.inEntity()) {
1041                         MessageDialog
1042                                         .openInformation(
1043                                                         shell,
1044                                                         Messages
1045                                                                         .getString("ERROR"), //$NON-NLS-1$
1046                                                         Messages
1047                                                                         .getString("CANNOT_MODIFY_ENTITY")); //$NON-NLS-1$
1048                         return true;
1049                 }
1050                 return false;
1051         }
1052 
1053         /***
1054          * Checks if given node is in entity. If yes then error message is
1055          * returned.
1056          * 
1057          * @param node
1058          *                node to check
1059          * @return <code>not null</code> if node is in entity,
1060          *         <code>null</code> otherwise.
1061          */
1062         public String checkInEntity(final Node node) {
1063                 if (DOMUtils.isInEntity(node)) {
1064                         return Messages.getString("CANNOT_MODIFY_ENTITY"); //$NON-NLS-1$
1065                 }
1066                 return null;
1067         }
1068 
1069         /***
1070          * Checks if given pointer points into entity. If yes then error message
1071          * is returned.
1072          * 
1073          * @param ptr
1074          *                the pointer to check
1075          * @return <code>not null</code> if node is in entity,
1076          *         <code>null</code> otherwise.
1077          */
1078         public String checkInEntity(final DomPointer ptr) {
1079                 if (ptr.inEntity()) {
1080                         return Messages.getString("CANNOT_MODIFY_ENTITY"); //$NON-NLS-1$
1081                 }
1082                 return null;
1083         }
1084 
1085         /***
1086          * Deletes given text node by wizard.
1087          * 
1088          * @param shell
1089          *                parent window, should not be <code>null</code>.
1090          * @param node
1091          *                node to delete, must not be <code>null</code>. May
1092          *                be text, cdata or pure-text entity only.
1093          * @throws ExportException
1094          *                 if shit happens.
1095          */
1096         public void deleteTextNode(final Shell shell, final Node node)
1097                         throws ExportException {
1098                 if (!DOMUtils.isTextualNode(node))
1099                         throw new IllegalArgumentException("illegal node type");//$NON-NLS-1$
1100                 if (checkInEntity(shell, node)) {
1101                         return;
1102                 }
1103                 // verify if there are some neighbour elements. If yes, then the
1104                 // rule
1105                 // for this text node may be AnyString only hence it can be
1106                 // deleted.
1107                 final Element e = (Element) node.getParentNode();
1108                 if (!DOMUtils.containsElements(e)) {
1109                         // check if element's contents stays valid when we
1110                         // remove the node.
1111                         final ElementRule rule = xmlAccess.getSchema()
1112                                         .getElementRule(e);
1113                         final IValueRule valRule = rule.getValueRule();
1114                         final String newValue = DOMUtils.getTextContents(e,
1115                                         new NodeFilter() {
1116                                                 /*
1117                                                  * (non-Javadoc)
1118                                                  * 
1119                                                  * @see org.w3c.dom.traversal.NodeFilter#acceptNode(org.w3c.dom.Node)
1120                                                  */
1121                                                 public short acceptNode(Node n) {
1122                                                         if (node.isSameNode(n))
1123                                                                 return NodeFilter.FILTER_REJECT;
1124                                                         return NodeFilter.FILTER_ACCEPT;
1125                                                 }
1126                                         });
1127                         if (!valRule.acceptsValue(newValue)) {
1128                                 // nope, new value not accepted. try to delete
1129                                 // parent element.
1130                                 if (MessageDialog
1131                                                 .openQuestion(
1132                                                                 shell,
1133                                                                 Messages
1134                                                                                 .getString("QUESTION"), //$NON-NLS-1$
1135                                                                 Messages
1136                                                                                 .getString("CANT_DELETE_TEXT"))) //$NON-NLS-1$
1137                                 {
1138                                         deleteElements(shell, Collections
1139                                                         .singleton(e));
1140                                 }
1141                                 return;
1142                         }
1143                 }
1144                 // OK we may delete the text node.
1145                 xmlAccess.getUndoManager().mark();
1146                 final DocumentModifier dm = xmlAccess.getModifier();
1147                 dm.startModify();
1148                 xmlAccess.getUndoManager().mark();
1149                 dm.remove(node);
1150                 xmlAccess.getUndoManager().mark();
1151                 dm.endModify();
1152         }
1153 
1154         /***
1155          * Tries to delete a single node.
1156          * 
1157          * @param shell
1158          *                parent shell, should not be <code>null</code>.
1159          * @param node
1160          *                node to delete.
1161          * @throws ExportException
1162          *                 if shit happens.
1163          */
1164         public void deleteNode(final Shell shell, final Node node)
1165                         throws ExportException {
1166                 if (checkInEntity(shell, node))
1167                         return;
1168                 // attribute?
1169                 if (node.getNodeType() == Node.ATTRIBUTE_NODE) {
1170                         deleteAttribute(shell, (Attr) node);
1171                         return;
1172                 }
1173                 // textual?
1174                 if (DOMUtils.isTextualEntity(node)) {
1175                         deleteTextNode(shell, node);
1176                         return;
1177                 }
1178                 // element?
1179                 if (node.getNodeType() == Node.ELEMENT_NODE) {
1180                         deleteElements(shell, Collections
1181                                         .singleton((Element) node));
1182                         return;
1183                 }
1184                 // entity ref?
1185                 if (node.getNodeType() == Node.ENTITY_REFERENCE_NODE) {
1186                         final int index = DOMUtils.getNodeIndex(node);
1187                         final Element parent = (Element) node.getParentNode();
1188                         final ElementRule rule = xmlAccess.getSchema()
1189                                         .getElementRule(parent);
1190                         final DOMPoint start = new DOMPoint(index, 0);
1191                         final DOMPoint end = new DOMPoint(index + 1, 0);
1192                         final String errorMsg = rule.isDeletable(start, end);
1193                         if (errorMsg != null) {
1194                                 MessageDialog
1195                                                 .openError(
1196                                                                 shell,
1197                                                                 Messages
1198                                                                                 .getString("ERROR"), //$NON-NLS-1$
1199                                                                 Messages
1200                                                                                 .getString("CANT_DELETE_ENTITY") + //$NON-NLS-1$
1201                                                                                 "\n"
1202                                                                                 + errorMsg); //$NON-NLS-1$
1203                                 return;
1204                         }
1205                 }
1206                 // if the node is not entityref, textual nor element then it may
1207                 // be
1208                 // removed right away.
1209                 xmlAccess.getUndoManager().mark();
1210                 final DocumentModifier dm = xmlAccess.getModifier();
1211                 dm.startModify();
1212                 dm.remove(node);
1213                 xmlAccess.getUndoManager().mark();
1214                 dm.endModify();
1215         }
1216 
1217         /***
1218          * Tests if fragment can be inserted.
1219          * 
1220          * @param ptr
1221          *                place of insertion
1222          * @param fragment
1223          *                to insert
1224          * @return null if can be inserted, otherwise error message
1225          */
1226         public String canInsertFragment(final DomPointer ptr,
1227                         final DocumentFragment fragment) {
1228                 String result = checkInEntity(ptr);
1229                 if (result != null)
1230                         return result;
1231                 final ElementRule rule = xmlAccess.getSchema().getElementRule(
1232                                 (Element) ptr.parent);
1233                 return rule.isInsertable(ptr.ip, fragment);
1234         }
1235 
1236         /***
1237          * Tries to insert given document fragment into given position.
1238          * 
1239          * @param shell
1240          *                parent shell, should not be <code>null</code>.
1241          * @param ptr
1242          *                the pointer where the fragment will be inserted. Must
1243          *                not be <code>null</code>.
1244          * @param fragment
1245          *                the fragment to insert. A clone of this fragment is
1246          *                inserted hence the fragment is not modified by this
1247          *                method. Must not be <code>null</code>.
1248          * @throws ExportException
1249          */
1250         public void insertFragment(final Shell shell, final DomPointer ptr,
1251                         final DocumentFragment fragment) throws ExportException {
1252                 if (ptr == null)
1253                         throw new NullArgumentException("ptr"); //$NON-NLS-1$
1254                 if (fragment == null)
1255                         throw new NullArgumentException("fragment"); //$NON-NLS-1$
1256                 final String errorMsg = canInsertFragment(ptr, fragment);
1257                 if (errorMsg != null) {
1258                         if (!MessageDialog
1259                                         .openQuestion(
1260                                                         shell,
1261                                                         Messages
1262                                                                         .getString("ERROR"), //$NON-NLS-1$
1263                                                         Messages
1264                                                                         .getString("FRAGMENT_NOT_INSERTABLE") + //$NON-NLS-1$
1265                                                                         "\n"
1266                                                                         + errorMsg)) //$NON-NLS-1$
1267                                 return;
1268                         final String textContents = fragment.getTextContent();
1269                         insertTextAt(shell, ptr, textContents, Node.TEXT_NODE);
1270                         return;
1271                 }
1272                 // clone and insert
1273                 final DocumentFragment clonedFragment = (DocumentFragment) fragment
1274                                 .cloneNode(true);
1275                 xmlAccess.getUndoManager().mark();
1276                 xmlAccess.getModifier().startModify();
1277                 DOMMutils.insertFragment(clonedFragment, ptr);
1278                 xmlAccess.getUndoManager().mark();
1279                 xmlAccess.getModifier().endModify();
1280         }
1281 
1282         /***
1283          * Tests if text can be inserted.
1284          * 
1285          * @param ptr
1286          *                place of insertion
1287          * @param string
1288          *                to insert
1289          * @return null if can be inserted, otherwise error message
1290          */
1291         public String canInsertTextAt(final DomPointer ptr, final String string) {
1292                 final ElementRule rule = xmlAccess.getSchema().getElementRule(
1293                                 (Element) ptr.parent);
1294                 final IValueRule validator = rule.newTextValidator(ptr.ip,
1295                                 ptr.ip);
1296                 return validator.getErrorMessage(string);
1297         }
1298 
1299         /***
1300          * Tries to insert text at specified location.
1301          * 
1302          * @param shell
1303          *                parent shell, should not be <code>null</code>.
1304          * @param ptr
1305          *                the pointer where the fragment will be inserted. Must
1306          *                not be <code>null</code>.
1307          * @param string
1308          *                a string to insert.
1309          * @param type
1310          *                the type of node, {@link Node#TEXT_NODE} or
1311          *                {@link Node#CDATA_SECTION_NODE}.
1312          * @throws ExportException
1313          */
1314         public void insertTextAt(final Shell shell, final DomPointer ptr,
1315                         final String string, final short type)
1316                         throws ExportException {
1317                 final String error = canInsertTextAt(ptr, string);
1318                 if (error != null) {
1319                         MessageDialog
1320                                         .openError(
1321                                                         shell,
1322                                                         Messages
1323                                                                         .getString("ERROR"), //$NON-NLS-1$
1324                                                         Messages
1325                                                                         .getString("TEXT_CANNOT_BE_INSERTED_HERE") //$NON-NLS-1$
1326                                                                         + error);
1327                         return;
1328                 }
1329                 xmlAccess.getModifier().startModify();
1330                 DOMMutils.insertText(ptr, string, type);
1331                 xmlAccess.getModifier().endModify();
1332         }
1333 
1334         /***
1335          * Tries to delete text from specified location.
1336          * 
1337          * @param shell
1338          *                parent shell, should not be <code>null</code>.
1339          * @param cut
1340          *                the interval denoting text being deleted or replaced.
1341          *                Must not be <code>null</code>. Caller must ensure
1342          *                that there are no elements covered by this interval.
1343          *                between the two pointers.
1344          * @param replaceWith
1345          *                a string that will replace given interval, may be
1346          *                <code>null</code>.
1347          * @throws ExportException
1348          */
1349         public void deleteReplaceText(final Shell shell, final DOMInterval cut,
1350                         final String replaceWith) throws ExportException {
1351                 final IInputValidator validator = SchematicUtils
1352                                 .createTextValidator(xmlAccess.getSchema(), cut);
1353                 final String error = validator.isValid(replaceWith);
1354                 if (error != null) {
1355                         final String errorMsg = StringUtils
1356                                         .isEmpty(replaceWith) ? sk.uniba.euromath.document.lang.Messages
1357                                         .getString("TEXT_CANNOT_BE_DELETED") //$NON-NLS-1$
1358                                         : sk.uniba.euromath.document.lang.Messages
1359                                                         .getString("TEXT_CANNOT_BE_REPLACED"); //$NON-NLS-1$
1360                         MessageDialog.openError(shell, Messages
1361                                         .getString("ERROR"), //$NON-NLS-1$
1362                                         errorMsg + "\n" + error); //$NON-NLS-1$
1363                         return;
1364                 }
1365                 xmlAccess.getModifier().startModify();
1366                 DOMMutils.remove(cut);
1367                 if (!StringUtils.isEmpty(replaceWith))
1368                         DOMMutils.insertText(cut.from.ip,
1369                                         cut.from.parentElement, replaceWith,
1370                                         Node.TEXT_NODE);
1371                 xmlAccess.getModifier().endModify();
1372         }
1373 }