This sample command is a slightly simplified version of command convertCase
in XMLmind XML Editor - Commands.
Excerpts from ConvertCaseCmd.java
:
public class ConvertCaseCmd extends CommandBase { private enum Op { LOWER, UPPER, CAPITAL } private Op op; private TextNode beginText; private int beginOffset; private TextNode endText; private int endOffset; public ConvertCaseCmd() { super(/*repeatable*/ false, /*recordable*/ true); } public boolean prepare(DocumentView docView, String parameter, int x, int y) { op = null; beginText = endText = null; beginOffset = endOffset = -1; MarkManager markManager = docView.getMarkManager(); if (markManager == null) { return false; } if ("lower".equals(parameter)) { op = Op.LOWER; } else if ("upper".equals(parameter)) { op = Op.UPPER; } else if ("capital".equals(parameter)) { op = Op.CAPITAL; } else { return false; } NodeMark selected = markManager.getSelected(); if (selected != null) { Node selectedNode = selected.getNode(); NodeMark selected2 = markManager.getSelected2(); if (selected2 != null && selected2.getNode() != selectedNode) { // Not implemented. return false; } beginText = (TextNode) Traversal.traverse(selectedNode, Traversal.textNodeFinder); if (beginText == null) { // Does not contain text. return false; } beginOffset = 0; endText = (TextNode) Traversal.traverseBackwards(selectedNode, Traversal.textNodeFinder); // endText cannot be null because beginText is not null. endOffset = endText.getTextLength(); if (beginText == endText && beginOffset == endOffset) { // Single empty text node: nothing to convert. return false; } } else { TextLocation dot = markManager.getDot(); if (dot == null) { // Document does not contain text. return false; } beginText = dot.getTextNode(); beginOffset = dot.getOffset(); TextLocation mark = markManager.getMark(); if (mark != null) { endText = mark.getTextNode(); endOffset = mark.getOffset(); } else { endText = beginText; endOffset = beginOffset; } if (beginText == endText) { if (beginOffset == endOffset) { // Not text selection: convert to end of word. int count = beginText.getTextLength(); while (endOffset < count) { if (XMLText.isXMLSpace( beginText.getTextChar(endOffset))) { break; } ++endOffset; } if (endOffset == beginOffset) { // Nothing to convert (empty text node or caret at end // of word). return false; } } else if (beginOffset > endOffset) { int swap; swap = beginOffset; beginOffset = endOffset; endOffset = swap; } } } return true; }
Initializes the instance variables of | |
If a | |
Parameter is parsed and parsed value is assigned to instance variable | |
Case where nodes are selected. Selection of a node range is not supported by If selected node does not contain textual nodes or if selected element only contains a single empty textual node, | |
Case where there is a text selection. | |
Case where there is no text selection (or when |
public CommandResult doExecute(DocumentView docView, String parameter, int x, int y) { MarkManager markManager = docView.getMarkManager(); markManager.beginMark(); boolean swapped = false; if (beginText == endText) { convertCase(beginText, beginOffset, endOffset - beginOffset); } else { Document doc = docView.getDocument(); doc.beginEdit(); TextRangeList rangeList = new TextRangeList(); // Note that TextCollector handles the case where beginText is // after endText. TextCollector.Status status = (new TextCollector()).collect(beginText, beginOffset, endText, endOffset, rangeList); swapped = (status == TextCollector.Status.COLLECTED_AFTER_SWAP); TextRange[] list = rangeList.list; int count = rangeList.size; for (int i = 0; i < count; ++i) { TextRange range = list[i]; convertCase(range.text, range.offset, range.count); } doc.endEdit(); } docView.describeUndo("CONVERT TO " + op.toString() + "CASE"); markManager.remove(Mark.SELECTED); markManager.remove(Mark.SELECTED2); markManager.remove(Mark.MARK); if (swapped) { markManager.getDot().moveTo(beginText, beginOffset); } else { markManager.getDot().moveTo(endText, endOffset); } markManager.endMark(); op = null; beginText = endText = null; beginOffset = endOffset = -1; return CommandResult.DONE; } private void convertCase(TextNode text, int offset, int count) { char[] chars = text.getTextChars(); switch (op) { case LOWER: text.replaceText(offset, count, (new String(chars, offset, count)).toLowerCase()); break; case UPPER: text.replaceText(offset, count, (new String(chars, offset, count)).toUpperCase()); break; default: { char[] replacement = new char[count]; System.arraycopy(chars, offset, replacement, 0, count); boolean firstCharOfWord = true; for (int i = 0; i < count; ++i) { char c = replacement[i]; if (XMLText.isXMLSpace(c)) { firstCharOfWord = true; } else { if (firstCharOfWord) { firstCharOfWord = false; replacement[i] = Character.toUpperCase(c); } else { replacement[i] = Character.toLowerCase(c); } } } text.replaceText(offset, count, new String(replacement)); } } }
Using When the editing context changes, some commands can no longer be executed and other commands which were disabled, now become executable. This is the definition and the sole purpose of the An application such as XXE invokes the | |
Optimized case: case conversion from caret position to end of word. Note that the general case could have handled this simple case perfectly well. | |
The | |
| |
General case: case conversion of all text ranges found by | |
The | |
An editing command always updates the marks once it has finished its job. There is no general rule: a command must try to update the marks in a way which clearly indicates to the user what has been done. | |
It is generally a good idea to reinitialize the instance variables at the end of |