1
2
3
4
5
6
7
8
9
10
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.eclipse.jface.dialogs.InputDialog;
26 import org.eclipse.jface.dialogs.MessageDialog;
27 import org.eclipse.jface.window.Window;
28 import org.eclipse.swt.widgets.Display;
29 import org.eclipse.swt.widgets.Shell;
30 import org.w3c.dom.Attr;
31 import org.w3c.dom.Element;
32 import org.w3c.dom.EntityReference;
33 import org.w3c.dom.Node;
34 import org.w3c.dom.traversal.NodeFilter;
35
36 import sk.baka.ikslibs.DOMUtils;
37 import sk.baka.ikslibs.ids.IDManager;
38 import sk.baka.ikslibs.levelmapper.NodeListID;
39 import sk.baka.ikslibs.modify.DOMMutils;
40 import sk.baka.ikslibs.ptr.DOMPoint;
41 import sk.baka.ikslibs.ptr.DomPointer;
42 import sk.baka.ikslibs.ptr.DomPointerFactory;
43 import sk.baka.xml.gene.ExportException;
44 import sk.uniba.euromath.document.schema.AttributeRule;
45 import sk.uniba.euromath.document.schema.ElementRule;
46 import sk.uniba.euromath.document.schema.InsertList;
47 import sk.uniba.euromath.document.schema.INameList;
48 import sk.uniba.euromath.document.schema.NewElementRule;
49 import sk.uniba.euromath.document.schema.plug.IValueRule;
50 import sk.uniba.euromath.editor.dialogs.Dialogs;
51 import sk.uniba.euromath.editor.dialogs.WidgetWrapperDialog;
52 import sk.uniba.euromath.editor.lang.Messages;
53 import sk.uniba.euromath.editor.widgets.EntityList;
54 import sk.uniba.euromath.editor.widgets.namelist.DisplayableNameItemTypeEnum;
55 import sk.uniba.euromath.editor.widgets.namelist.DisplayableNameList;
56 import sk.uniba.euromath.editor.widgets.namelist.DisplayableNameListImpl;
57 import sk.uniba.euromath.editor.widgets.namelist.NameListItemChooser;
58 import sk.uniba.euromath.tools.StringTools;
59
60 /***
61 * Provides basic editing features, document modification driven by schema.
62 *
63 * @author Martin Vysny
64 * @author Tomáš Studva
65 * @TODO MOTO> Move to .editor package.
66 */
67 public final class DocumentModifyHelper {
68 /***
69 * Constuctor.
70 *
71 * @param xmlAccess
72 * the document instance.
73 */
74 public DocumentModifyHelper(XMLAccess xmlAccess) {
75 super();
76 this.xmlAccess = xmlAccess;
77 }
78
79 /***
80 * The document instance.
81 */
82 private final XMLAccess xmlAccess;
83
84 /***
85 * Used to c
86 */
87 private DisplayableNameListImpl<NewElementRule> dnl = null;
88
89 /***
90 * Manages the process of inserting a new attribute into given element.
91 *
92 * @param parent
93 * parent window. Should not be <code>null</code>.
94 * @param e
95 * the element where to insert new attribute.
96 * @throws ExportException
97 * when document modification finalization fails.
98 */
99 public void insertNewAttribute(Shell parent, Element e) throws ExportException
100 {
101 INameList<AttributeRule> list = xmlAccess.getSchema().getElementRule(e)
102 .getInsertableAttributes();
103 if (list.getForeignNames().size() + list.getLocalNames().size() == 0) {
104 MessageDialog
105 .openInformation(
106 parent,
107 Messages.getString("INFORMATION"), Messages.getString("NO_INSERTABLE_ATTRIBUTE"));
108 return;
109 }
110 WidgetWrapperDialog<NameListItemChooser<AttributeRule>> dlg = Dialogs
111 .createAttributeCreator(parent, list, xmlAccess, xmlAccess
112 .getNsManager());
113 if (dlg.open() != Window.OK)
114 return;
115
116 xmlAccess.getUndoManager().mark();
117 xmlAccess.getModifier().startModify();
118 xmlAccess.getModifier().createAttribute(e,
119 dlg.getWidget().getSelectedQName(),
120 dlg.getWidget().getSelectedValue());
121 xmlAccess.getModifier().endModify();
122 }
123
124 /***
125 * Manages the process of modifying the attribute value.
126 *
127 * @param parent
128 * parent window. Should not be <code>null</code>.
129 * @param attr
130 * the attribute whose value has to be modified.
131 * @throws ExportException
132 * when document modification finalization fails.
133 */
134 public void modifyAttribute(Shell parent, Attr attr) throws ExportException {
135 AttributeRule rule = xmlAccess.getSchema().getElementRule(
136 attr.getOwnerElement()).getAttributeRule(attr);
137 WidgetWrapperDialog<NameListItemChooser<AttributeRule>> dlg = Dialogs
138 .createAttributeEditor(parent, attr, rule, xmlAccess, xmlAccess
139 .getNsManager());
140 if (dlg.open() != Window.OK)
141 return;
142
143 xmlAccess.getUndoManager().mark();
144 xmlAccess.getModifier().startModify();
145 xmlAccess.getModifier().setText(attr,
146 dlg.getWidget().getSelectedValue());
147 xmlAccess.getModifier().endModify();
148 }
149
150 /***
151 * Manages the process of deleting the attribute.
152 *
153 * @param parent
154 * parent window. Should not be <code>null</code>.
155 * @param attr
156 * the attribute which has to be deleted.
157 * @throws ExportException
158 * when document modification finalization fails.
159 */
160 public void deleteAttribute(Shell parent, Attr attr) throws ExportException {
161 if (checkInEntity(parent, attr))
162 return;
163 if (!xmlAccess.getSchema().getElementRule(attr.getOwnerElement())
164 .isDeletableAttribute(attr)) {
165
166 MessageDialog
167 .openError(parent,
168 Messages.getString("ERROR"),
169 Messages
170 .getString(
171 "ATTRIBUTE_CANNOT_BE_REMOVED", DOMUtils.printQName(attr)));
172 return;
173 }
174
175 final boolean result = MessageDialog
176 .openQuestion(
177 parent,
178 Messages.getString("QUESTION"), Messages.getString("REMOVE_ATTRIBUTE_QUESTION", DOMUtils.printQName(attr)));
179
180 if (!result)
181 return;
182
183 xmlAccess.getUndoManager().mark();
184 xmlAccess.getModifier().startModify();
185 xmlAccess.getModifier().remove(attr);
186 xmlAccess.getModifier().endModify();
187 }
188
189 /***
190 * Manages the process of deleting the elements. All elements must have the
191 * same parent.
192 *
193 * @param shell
194 * parent window. Should not be <code>null</code>.
195 * @param elements
196 * set of <code>Element</code> s that must be deleted.
197 * @throws ExportException
198 * when document modification finalization fails.
199 */
200 public void deleteElements(Shell shell, Set<Element> elements)
201 throws ExportException {
202 if (elements.size() == 0)
203 throw new IllegalArgumentException(
204 "The set of elements must not be empty.");
205
206 Node parent = null;
207 for (Element e : elements) {
208 if (parent == null)
209 parent = e.getParentNode();
210 else {
211 if (!parent.isSameNode(e.getParentNode()))
212 throw new IllegalArgumentException(
213 "The elements have no common parent.");
214 }
215 }
216 if (parent.getNodeType() != Node.ELEMENT_NODE) {
217 MessageDialog.openError(shell, Messages.getString("ERROR"),
218 Messages.getString("CANNOT_DELETE_ROOT"));
219 return;
220 }
221 ElementRule rule = xmlAccess.getSchema().getElementRule(
222 (Element) parent);
223 DeleteElementsManager dem = new DeleteElementsManager();
224 if (!dem.execute(shell, elements, DOMUtils.printQName(parent), rule))
225 return;
226
227 if (dem.result == null) {
228 Set<Element> delete = new HashSet<Element>();
229 delete.add((Element) parent);
230 deleteElements(shell, delete);
231 return;
232 }
233 if (dem.result.size() != 0) {
234 Set<Element> delete = new HashSet<Element>(elements);
235 delete.addAll(dem.result);
236 elements = delete;
237 }
238
239 xmlAccess.getUndoManager().mark();
240 xmlAccess.getModifier().startModify();
241
242 for (Element e : elements) {
243 xmlAccess.getModifier().remove(e);
244 xmlAccess.getIdManager().removeTree(e);
245 }
246 xmlAccess.getModifier().endModify();
247 }
248
249 /***
250 * Manages the process of element deletion.
251 *
252 * @author Martin Vysny
253 */
254 private static class DeleteElementsManager {
255 /***
256 * List of elements that must be deleted aswell. If the set is empty
257 * then no other elements must be deleted. If the set is
258 * <code>null</code> then the parent element must be deleted. Valid
259 * after the <code>execute()</code> function returns <code>true</code>.
260 */
261 List<? extends Element> result = null;
262
263 /***
264 * Drives the user through the process.
265 *
266 * @param parent
267 * parent window.
268 * @param elements
269 * the list of elements that have to be deleted.
270 * @param parentName
271 * the name of the parent element. Used only to display the
272 * name on the shell.
273 * @param parentRule
274 * rule for parent element.
275 * @return true if user permitted the deletion, false if the request was
276 * rejected or an error occured.
277 */
278 boolean execute(Shell parent, Set<Element> elements, String parentName,
279 ElementRule parentRule) {
280 result = parentRule.areElementsDeletable(elements);
281 boolean dlgResult;
282 if (result == null) {
283 dlgResult = MessageDialog
284 .openQuestion(parent, Messages.getString("QUESTION"),
285 Messages.getString(
286 "REQ_PARENT_ELEMENT_DEL", parentName));
287 } else if (result.size() == 0) {
288 dlgResult = MessageDialog.openQuestion(parent, Messages
289 .getString("QUESTION"),
290 Messages.getString(
291 "ELEMENT_DELETE_QUERY", printList(elements)));
292 } else {
293 dlgResult = MessageDialog.openQuestion(parent, Messages
294 .getString("QUESTION"),
295 Messages.getString(
296 "REQ_ELEMENTS_DEL", printList(result)));
297 }
298 return dlgResult;
299 }
300
301 /***
302 * Prints given elements as a comma-separated list.
303 *
304 * @param elements
305 * the elements, whose names to print.
306 * @return comma separated element names.
307 */
308 private static String printList(Collection<? extends Element> elements) {
309 StringBuilder builder = new StringBuilder();
310 for (Element e : elements) {
311 if (builder.length() != 0)
312 builder.append(", ");
313 builder.append(DOMUtils.printQName(e));
314 }
315 return builder.toString();
316 }
317 }
318
319 /***
320 * Manages the process of enclosing the nodes. All nodes must have the same
321 * parent. Both pointers may point inside a text.
322 *
323 * @param start
324 * start of the interval. Must point before the <code>end</code>
325 * parameter.
326 * @param end
327 * end of the interval.
328 * @param parent
329 * parent window. Should not be <code>null</code>.
330 * @throws ExportException
331 * when document modification finalization fails.
332 */
333 public void encloseNodes(Shell parent, DomPointer start, DomPointer end)
334 throws ExportException {
335 if (!start.parent.isSameNode(end.parent))
336 throw new IllegalArgumentException(
337 "The pointers must point into one node.");
338 if (start.compareTo(end) >= 0)
339 throw new IllegalArgumentException(
340 "The end pointer must point after the start pointer.");
341 if (start.inEntity()) {
342
343 MessageDialog.openInformation(parent, Messages.getString("ERROR"),
344 Messages.getString("CANNOT_MODIFY_ENTITY"));
345 return;
346 }
347 final ElementRule rule = xmlAccess.getSchema().getElementRule(
348 (Element) start.parent);
349 final INameList<NewElementRule> elements = rule.getEnclosingElements(
350 start.ip, end.ip);
351 if (elements == null) {
352
353 MessageDialog.openInformation(parent, Messages.getString("ERROR"),
354 Messages.getString("ENCLOSE_FORBIDDEN"));
355 return;
356 }
357 final DisplayableNameList<NewElementRule> dnl = new DisplayableNameListImpl<NewElementRule>(
358 elements, xmlAccess, xmlAccess.getNsManager(), true);
359 final WidgetWrapperDialog<NameListItemChooser<NewElementRule>> dlg = Dialogs
360 .createElementCreator(parent, xmlAccess, Messages
361 .getString("ENCLOSE_NODES"), dnl);
362 if (dlg.open() != Window.OK)
363 return;
364
365 xmlAccess.getUndoManager().mark();
366 xmlAccess.getModifier().startModify();
367 final QName qname = dlg.getWidget().getSelectedQName();
368 Element e = xmlAccess.getDocument().createElementNS(
369 qname.getNamespaceURI(), DOMUtils.printQName(qname));
370
371 Node last;
372
373 if (end.ip.pos != 0) {
374 last = DOMMutils.splitText(end);
375 } else
376 last = end.pointsTo;
377
378 DOMMutils.insertNode(e, start, false);
379 Node first = e.getNextSibling();
380
381 while (first != last) {
382 Node next = first.getNextSibling();
383 DOMMutils.remove(first);
384 DOMMutils.insertNode(first,
385 DomPointerFactory.create(e, (Node) null), false);
386 first = next;
387 }
388 xmlAccess.getModifier().endModify();
389 }
390
391 /***
392 * Manages the process of declosing the nodes - replaces given element with
393 * its contents.
394 *
395 * @param parent
396 * parent window. Should not be <code>null</code>.
397 * @param e
398 * the element, that shall be replaced by its children if the
399 * declosing is allowed.
400 * @throws ExportException
401 * when document modification finalization fails.
402 */
403 public void decloseNodes(Shell parent, Element e) throws ExportException {
404 if ((e.getParentNode() == null)
405 || (e.getParentNode().getNodeType() != Node.ELEMENT_NODE)) {
406
407 MessageDialog.openInformation(parent, Messages
408 .getString("INFORMATION"),
409 Messages.getString("DECLOSE_FORBIDDEN"));
410 return;
411 }
412 ElementRule rule = xmlAccess.getSchema().getElementRule(
413 (Element) e.getParentNode());
414 if (!rule.isDeclosable(e)) {
415
416 MessageDialog.openInformation(parent, Messages
417 .getString("INFORMATION"),
418 Messages.getString("DECLOSE_FORBIDDEN"));
419 return;
420 }
421 final boolean result = MessageDialog.openQuestion(parent, "Question",
422 Messages.getString("DECLOSE_QUERY", DOMUtils.printQName(e)));
423 if (!result)
424 return;
425
426 xmlAccess.getUndoManager().mark();
427 xmlAccess.getModifier().startModify();
428 Node node = e.getFirstChild();
429 Node insertAfter = e;
430 while (node != null) {
431 Node next = node.getNextSibling();
432 xmlAccess.getModifier().remove(node);
433 DomPointer ptr = DomPointerFactory.create(insertAfter).getNextSibling();
434
435
436 DOMMutils.insertNode(node, ptr, next == null);
437 insertAfter = node;
438 node = next;
439 }
440
441 xmlAccess.getModifier().remove(e);
442 xmlAccess.getModifier().endModify();
443 }
444
445 /***
446 * Manages the process of inserting an entity into selected position by
447 * dialog.
448 *
449 * @param ptr
450 * the desired place.
451 * @param parent
452 * parent window. Should not be <code>null</code>.
453 * @throws ExportException
454 * when document modification finalization fails.
455 */
456 public void insertEntity(Shell parent, DomPointer ptr)
457 throws ExportException {
458
459 List<String> validEntities = getInsertableEntities(parent, ptr);
460
461 WidgetWrapperDialog<EntityList> dlg = Dialogs.createEntityLister(
462 parent, xmlAccess,
463 Messages.getString("CREATE_ENTITY"), validEntities);
464 if (dlg.open() != Window.OK)
465 return;
466 final String entityName = dlg.getWidget().getEntityName();
467
468 insertEntity(entityName, ptr);
469 }
470
471 /***
472 * Inserts entity to given position.
473 *
474 * @param entityName
475 * the entity name.
476 * @param ptr
477 * the insert point.
478 * @throws ExportException
479 * if document modification finalization fails.
480 */
481 public void insertEntity(String entityName, DomPointer ptr)
482 throws ExportException {
483 xmlAccess.getUndoManager().mark();
484 xmlAccess.getModifier().startModify();
485 EntityReference er = xmlAccess.getDocument().createEntityReference(
486 entityName);
487 DOMMutils.insertNode(er, ptr, false);
488 xmlAccess.getModifier().endModify();
489 }
490
491 /***
492 * Computes insertable entities. Shows various warning dialogs if no
493 * entities can be found etc.
494 *
495 * @param ptr
496 * the desired place.
497 * @param parent
498 * parent window. Can be <code>null</code>.
499 * @return entity names that are insertable at given pointer. May return
500 * <code>null</code> if no entities are suitable.
501 */
502 public List<String> getInsertableEntities(final Shell parent,
503 final DomPointer ptr) {
504 if (ptr == null)
505 throw new IllegalArgumentException("ptr is null.");
506 if (ptr.inEntity()) {
507
508 if (parent != null)
509 MessageDialog
510 .openInformation(
511 parent,
512 Messages.getString("INFORMATION"), Messages.getString("CANNOT_MODIFY_ENTITY"));
513 return null;
514 }
515 final ElementRule rule = xmlAccess.getSchema().getElementRule(
516 ptr.parentElement);
517
518 Set<String> entities = xmlAccess.getEntityManager().getEntityNames();
519 if (entities.isEmpty()) {
520
521 if (parent != null)
522 MessageDialog
523 .openInformation(
524 parent,
525 Messages.getString("INFORMATION"), Messages.getString("NO_KNOWN_ENTITIES"));
526 return null;
527 }
528
529 final List<String> validEntities = new ArrayList<String>(entities
530 .size());
531 for (final String entityName : entities) {
532 if (rule.isInsertable(ptr.ip, entityName))
533 validEntities.add(entityName);
534 }
535 if (validEntities.isEmpty()) {
536
537 if (parent != null)
538 MessageDialog
539 .openInformation(
540 parent,
541 Messages.getString("INFORMATION"), Messages.getString("NO_INSERTABLE_ENTITY"));
542 return null;
543 }
544 return validEntities;
545 }
546
547 /***
548 * Computes DisplayableNameList of insertable elements.
549 *
550 * @param pointer
551 * to place where elements should be able to insert.
552 * @deprecated use getInsertableElementsQNames(DomPointer) instead. To
553 * display qname use {@link DomTools#printQName(QName)}.
554 */
555 @Deprecated
556 public DisplayableNameList getInsertableElements(final DomPointer pointer) {
557 if (pointer == null)
558 throw new IllegalArgumentException("pointer is null.");
559 final List<InsertList> insertLists = xmlAccess.getSchema()
560 .getElementRule(pointer.parentElement).getInsertableElements(
561 pointer.ip);
562
563 final InsertList insertList = insertLists.get(0);
564 for (final sk.uniba.euromath.document.schema.ElementLoc loc : insertList) {
565 dnl = new DisplayableNameListImpl<NewElementRule>(loc.nameList,
566 xmlAccess, xmlAccess.getNsManager(), true);
567 return dnl;
568 }
569 return null;
570 }
571
572 /***
573 * Computes list of names of elements, insertable at given pointer.
574 *
575 * @param pointer
576 * @return ordered list of qnames.
577 */
578 public List<QName> getInsertableElementsQNames(final DomPointer pointer) {
579 if (pointer == null)
580 throw new IllegalArgumentException("pointer is null.");
581
582
583
584 final List<InsertList> insertLists = xmlAccess.getSchema()
585 .getElementRule(pointer.parentElement).getInsertableElements(
586 pointer.ip);
587 final String localNamespace = pointer.parentElement.getNamespaceURI();
588
589 final TreeSet<QName> result = new TreeSet<QName>(
590 new Comparator<QName>() {
591 public int compare(QName o1, QName o2) {
592 if (DOMUtils.equalsURI(o1.getNamespaceURI(),
593 localNamespace)) {
594 if (!DOMUtils.equalsURI(o2.getNamespaceURI(),
595 localNamespace))
596 return -1;
597 return o1.getLocalPart().compareTo(
598 o2.getLocalPart());
599 }
600 if (DOMUtils.equalsURI(o2.getNamespaceURI(),
601 localNamespace))
602 return 1;
603
604 final int result = StringTools.nonNullStr(
605 o1.getPrefix()).compareTo(
606 StringTools.nonNullStr(o2.getPrefix()));
607 if (result != 0)
608 return result;
609 return o1.getLocalPart().compareTo(o2.getLocalPart());
610 }
611 });
612 for (final InsertList insertList : insertLists) {
613 final INameList<NewElementRule> nl = insertList
614 .getFirstNamelistAt(pointer.ip);
615 final DisplayableNameList<NewElementRule> dnl = new DisplayableNameListImpl<NewElementRule>(
616 nl, xmlAccess, xmlAccess.getNsManager(), true);
617 final List<QName> qnames = getDomQnames(dnl);
618 result.addAll(qnames);
619 }
620 return new ArrayList<QName>(result);
621 }
622
623 /***
624 * Computes QNames of elements from DisplayableNameList.
625 */
626 public List<QName> getDomQnames(DisplayableNameList dnl) {
627 List<QName> result = new ArrayList<QName>();
628 if (dnl != null) {
629 for (int i = 0; i < dnl.getLength(); i++) {
630
631 if (dnl.getType(i) == DisplayableNameItemTypeEnum.ITEM_NAMESPACE)
632 continue;
633 final String ns = dnl.getDomNamespaceUri(i);
634 final String prefix = xmlAccess.getNsManager()
635 .getBestPrefix(ns);
636 result.add(dnl.getDomQName(i, prefix));
637 }
638 }
639 return result;
640 }
641
642 /***
643 * Returns DomPointer which points to place in node tree to textual node
644 * with id nodeID and after text prevText. If nodeID is null or is not valid
645 * id of textual node then null is returned.
646 *
647 * @param nodeID
648 * id of textual node in which returned pointer points
649 * @param prevText
650 * text after which returned pointer points
651 *
652 */
653 public DomPointer getPointer(String nodeID, String prevText) {
654 if (nodeID == null)
655 return null;
656 final NodeListID nodeList = xmlAccess.getIdManager().getNodeNull(nodeID);
657 if (nodeList == null)
658 return null;
659
660
661 if (nodeList.isTextual()) {
662 int textValPos = nodeList.resolveIndex(prevText, prevText.length());
663 return nodeList.getPointer(textValPos, true, false);
664 }
665
666 return null;
667 }
668
669 /***
670 * Inserts text to end of element by dialog.
671 *
672 * @param element
673 * where to insert text node as child
674 * @param shell
675 * parent window. !!! Cannot be <code>null</code>.
676 * @throws DocumentException
677 * @throws ExportException
678 */
679 public void insertTextNode(Shell shell, Element element)
680 throws DocumentException, ExportException {
681 if (checkInEntity(shell, element))
682 return;
683 InputDialog dialog = new InputDialog(shell, "Insert text node action",
684 "Write text for text node:", "", null);
685
686 if (dialog.open() == Window.OK) {
687 String inputStr = dialog.getValue().trim();
688 DocumentModifier dm = xmlAccess.getModifier();
689
690 final DOMPoint ip = DomPointerFactory.create(element,
691 (Node) null).ip;
692
693 final String id = xmlAccess.getIdManager().getIDNull(element);
694 final Element contextElement=xmlAccess.getIdManager().getElement(id);
695 dm.startModify();
696 DOMMutils.insertText(ip, contextElement , inputStr, Node.TEXT_NODE);
697 dm.endModify();
698 }
699 }
700
701 /***
702 * Modifies text of node by dialog.
703 *
704 * @param node
705 * must be text node
706 * @param shell
707 * parent window. !!!Cannot be <code>null</code>.
708 * @throws ExportException
709 * if document fails to transform.
710 */
711 public void modifyTextNode(Shell shell, Node node) throws ExportException {
712 if (shell == null)
713 throw new IllegalArgumentException("Shell cannot be null.");
714 final InputDialog dialog = new InputDialog(Display.getCurrent()
715 .getActiveShell(), "Modify text of node", "Modify text", node
716 .getNodeValue(), null);
717 if (dialog.open() == Window.OK) {
718 xmlAccess.getUndoManager().mark();
719 final DocumentModifier dm = xmlAccess.getModifier();
720 dm.startModify();
721 dm.setText(node, dialog.getValue().trim());
722 dm.endModify();
723 }
724 }
725
726 /***
727 * Checks if given node is in entity. If yes then error dialog is shown.
728 *
729 * @param shell
730 * parent, should not be <code>null</code>.
731 * @param node
732 * node to check
733 * @return <code>true</code> if node is in entity, <code>false</code>
734 * otherwise.
735 */
736 public boolean checkInEntity(final Shell shell, final Node node) {
737 if (DOMUtils.isInEntity(node)) {
738 MessageDialog.openInformation(shell, Messages.getString("ERROR"),
739 Messages.getString("CANNOT_MODIFY_ENTITY"));
740 return true;
741 }
742 return false;
743 }
744
745 /***
746 * Deletes given text node.
747 *
748 * @param shell
749 * parent window, should not be <code>null</code>.
750 * @param node
751 * node to delete, must not be <code>null</code>. May be text,
752 * cdata or pure-text entity only.
753 * @throws ExportException
754 * if shit happens.
755 */
756 public void deleteTextNode(final Shell shell, final Node node)
757 throws ExportException {
758 if (!DOMUtils.isTextualNode(node))
759 throw new IllegalArgumentException("illegal node type");
760 if (checkInEntity(shell, node)) {
761 return;
762 }
763
764
765 final Element e = (Element) node.getParentNode();
766 if (!DOMUtils.containsElements(e)) {
767
768 final ElementRule rule = xmlAccess.getSchema().getElementRule(e);
769 final IValueRule valRule = rule.getValueRule();
770 final String newValue = DOMUtils.getTextContents(e,
771 new NodeFilter() {
772
773
774
775
776
777 public short acceptNode(Node n) {
778 if (node.isSameNode(n))
779 return NodeFilter.FILTER_REJECT;
780 return NodeFilter.FILTER_ACCEPT;
781 }
782 });
783 if (!valRule.acceptsValue(newValue)) {
784
785 if (MessageDialog.openQuestion(shell, Messages
786 .getString("QUESTION"),
787 Messages.getString("CANT_DELETE_TEXT")))
788 {
789 deleteElements(shell, Collections.singleton(e));
790 }
791 return;
792 }
793 }
794
795 xmlAccess.getUndoManager().mark();
796 final DocumentModifier dm = xmlAccess.getModifier();
797 dm.startModify();
798 dm.remove(node);
799 dm.endModify();
800 }
801
802 /***
803 * Tries to delete a single node.
804 *
805 * @param shell
806 * parent shell, should not be <code>null</code>.
807 * @param node
808 * node to delete.
809 * @throws ExportException
810 * if shit happens.
811 */
812 public void deleteNode(final Shell shell, final Node node)
813 throws ExportException {
814 if (checkInEntity(shell, node))
815 return;
816
817 if (node.getNodeType() == Node.ATTRIBUTE_NODE) {
818 deleteAttribute(shell, (Attr) node);
819 return;
820 }
821
822 if (DOMUtils.isTextualEntity(node)) {
823 deleteTextNode(shell, node);
824 return;
825 }
826
827 if (node.getNodeType() == Node.ELEMENT_NODE) {
828 deleteElements(shell, Collections.singleton((Element) node));
829 return;
830 }
831
832 if (node.getNodeType() == Node.ENTITY_REFERENCE_NODE) {
833 final int index = DOMUtils.getNodeIndex(node);
834 final Element parent = (Element) node.getParentNode();
835 final ElementRule rule = xmlAccess.getSchema().getElementRule(
836 parent);
837 final DOMPoint start = new DOMPoint(index, 0);
838 final DOMPoint end = new DOMPoint(index + 1, 0);
839 if (!rule.isDeletable(start, end)) {
840 MessageDialog.openError(shell, Messages.getString("ERROR"),
841 Messages.getString("CANT_DELETE_ENTITY"));
842 return;
843 }
844 }
845
846
847 xmlAccess.getUndoManager().mark();
848 final DocumentModifier dm = xmlAccess.getModifier();
849 dm.startModify();
850 dm.remove(node);
851 dm.endModify();
852 }
853 }