Source code: com/port80/eclipse/xml/editors/XMLEditor.java
1 package com.port80.eclipse.xml.editors;
2
3 import java.util.ArrayList;
4 import java.util.List;
5 import java.util.StringTokenizer;
6
7 import org.apache.xerces.util.XMLChar;
8 import org.apache.xerces.xni.XMLLocator;
9 import org.eclipse.jface.action.Action;
10 import org.eclipse.jface.action.IAction;
11 import org.eclipse.jface.action.IMenuManager;
12 import org.eclipse.jface.dialogs.IInputValidator;
13 import org.eclipse.jface.dialogs.InputDialog;
14 import org.eclipse.jface.preference.IPreferenceStore;
15 import org.eclipse.jface.resource.StringConverter;
16 import org.eclipse.jface.text.BadLocationException;
17 import org.eclipse.jface.text.IDocument;
18 import org.eclipse.jface.text.Position;
19 import org.eclipse.jface.text.TextSelection;
20 import org.eclipse.jface.text.source.IAnnotationModel;
21 import org.eclipse.jface.text.source.ISourceViewer;
22 import org.eclipse.jface.text.source.SourceViewer;
23 import org.eclipse.jface.util.PropertyChangeEvent;
24 import org.eclipse.swt.graphics.Color;
25 import org.eclipse.swt.graphics.Font;
26 import org.eclipse.swt.graphics.Point;
27 import org.eclipse.swt.graphics.RGB;
28 import org.eclipse.ui.IEditorInput;
29 import org.eclipse.ui.texteditor.ITextEditorActionConstants;
30 import org.eclipse.ui.views.contentoutline.IContentOutlinePage;
31 import org.w3c.dom.Document;
32 import org.w3c.dom.Node;
33
34 import com.port80.eclipse.editors.EditorsPlugin;
35 import com.port80.eclipse.editors.EditorsUtil;
36 import com.port80.eclipse.editors.IConstants;
37 import com.port80.eclipse.editors.ThemeManager;
38 import com.port80.eclipse.editors.util.CustomTextEditor;
39 import com.port80.eclipse.editors.util.NamedPosition;
40 import com.port80.eclipse.xml.parser.IXRange;
41 import com.port80.eclipse.xml.parser.XComment;
42 import com.port80.eclipse.xml.parser.XDocument;
43 import com.port80.eclipse.xml.parser.XDocumentType;
44 import com.port80.eclipse.xml.parser.XElementNS;
45 import com.port80.eclipse.xml.parser.XProcessingInstruction;
46 import com.port80.util.Sprint;
47
48 public class XMLEditor extends CustomTextEditor {
49
50 ////////////////////////////////////////////////////////////////////////
51
52 private static final String NAME = "XMLEditor";
53
54 ////////////////////////////////////////////////////////////////////////
55
56 private XMLEditorConfiguration fConfig;
57 private IEditorInput fInput;
58 private IPreferenceStore fPrefs;
59 private Document fModel;
60 private List fOutlineAttributes;
61 XMLOutlinePage fOutlinePage;
62
63 ////////////////////////////////////////////////////////////////////////
64
65 /**
66 * Constructor for SampleEditor.
67 */
68 public XMLEditor() {
69 super();
70 fInput = getEditorInput();
71 fPrefs = EditorsPlugin.getDefault().getPreferenceStore();
72 setPreferenceStore(fPrefs);
73 fConfig = new XMLEditorConfiguration(this, fPrefs);
74 setSourceViewerConfiguration(fConfig);
75 setDocumentProvider(new XMLDocumentProvider());
76 fOutlineAttributes = new ArrayList();
77 initOutlinePreferences();
78 }
79
80 private void initOutlinePreferences() {
81 fOutlineAttributes.clear();
82 String s = fPrefs.getString(IConstants.PREF_XML_OUTLINE_ATTRIBUTES);
83 StringTokenizer tok = new StringTokenizer(s);
84 while (tok.hasMoreTokens())
85 fOutlineAttributes.add(tok.nextToken());
86 }
87
88 ////////////////////////////////////////////////////////////////////////
89
90 /**
91 * @see org.eclipse.ui.texteditor.StatusTextEditor#updatePartControl(IEditorInput)
92 */
93 public void updatePartControl(IEditorInput input) {
94 super.updatePartControl(input);
95 if (input != fInput) {
96 EditorsPlugin.deleteProblemMarkers(getEditorInput(), IConstants.FormatActionProblemMarker);
97 fInput = input;
98 createModel();
99 refreshOutlineView();
100 }
101 }
102
103 public void dispose() {
104 super.dispose();
105 }
106
107 ////////////////////////////////////////////////////////////////////////
108
109 public XMLEditorConfiguration getConfiguration() {
110 return fConfig;
111 }
112
113 SourceViewer getViewer() {
114 return (SourceViewer) getSourceViewer();
115 }
116
117 public Node getModel() {
118 return fModel;
119 }
120
121 public void createModel() {
122 createModel(false);
123 }
124
125 public void createModel(boolean quiet) {
126 // For now, model is used only for outline page.
127 SourceViewer viewer = getViewer();
128 IDocument doc = viewer.getDocument();
129 if (!doc.containsPositionCategory(IConstants.POSITION_CATAGORY)) {
130 doc.addPositionCategory(IConstants.POSITION_CATAGORY);
131 doc.addPositionUpdater(EditorsUtil.getDefaultPositionUpdater());
132 } else {
133 EditorsUtil.removePositions(IConstants.POSITION_CATAGORY, viewer);
134 }
135 fModel = Util.parseXML(this, getViewer(), quiet);
136 if (fModel != null) {
137 int tabwidth = fConfig.getTabWidth(viewer);
138 addPositions(fModel, doc, tabwidth);
139 }
140 }
141
142 public void refreshOutlineView() {
143 if (fOutlinePage != null)
144 fOutlinePage.setInput(fModel);
145 }
146
147 public void outlinePageDisposed() {
148 fOutlinePage = null;
149 }
150
151 public Object getAdapter(Class adapter) {
152 if (IContentOutlinePage.class.equals(adapter)) {
153 if (fOutlinePage == null)
154 fOutlinePage = new XMLOutlinePage(this);
155 return fOutlinePage;
156 }
157 return super.getAdapter(adapter);
158 }
159
160 private void addPositions(Node node, IDocument doc, int tabwidth) {
161 if (node instanceof XElementNS) {
162 if (node.getNodeType() == Node.ELEMENT_NODE) {
163 XElementNS element = (XElementNS) node;
164 int start =
165 EditorsUtil.findOffset(
166 element.getStartLine(),
167 element.getStartColumn(),
168 getSourceViewer());
169 int end =
170 EditorsUtil.findOffset(
171 element.getEndLine(),
172 element.getEndColumn(),
173 getSourceViewer());
174 String name = "";
175 String s, value;
176 for (int i = 0; i < fOutlineAttributes.size(); ++i) {
177 s = (String) fOutlineAttributes.get(i);
178 value = element.getAttribute(s);
179 // getAttribute() return "" instead of null if attribute not exists!
180 if (value.length() != 0) {
181 if (name.length() == 0)
182 name = "\"" + value + "\"";
183 else
184 name += " \"" + value + "\"";
185 }
186 }
187 NamedPosition position =
188 new NamedPosition(
189 TreeObject.FOLDER,
190 element.getNodeName() + " " + name,
191 start,
192 end - start);
193 element.setUserData(position);
194 try {
195 doc.addPosition(IConstants.POSITION_CATAGORY, position);
196 } catch (Exception e) {
197 EditorsPlugin.log("Bad position or catagory", e);
198 }
199 }
200 }
201 for (Node child = node.getFirstChild(); child != null; child = child.getNextSibling())
202 addPositions(child, doc, tabwidth);
203 }
204
205 // CustomTextEditor ///////////////////////////////////////////////
206
207 protected void initBackground() {
208 String name = getPreferenceStore().getString(IConstants.PREF_XML_BG);
209 RGB rgb = StringConverter.asRGB(name);
210 name = Sprint.f("0x%06X").a(rgb.red << 16 | rgb.green << 8 | rgb.blue).end();
211 Color color = EditorsPlugin.getThemeManager().getColorFactory().create(name);
212 getSourceViewer().getTextWidget().setBackground(color);
213 }
214
215 protected void initFont() {
216 String fontspec = getPreferenceStore().getString(IConstants.PREF_XML_FONT);
217 Font font = EditorsPlugin.getThemeManager().getFontFactory().create(fontspec);
218 if (font == null) {
219 EditorsPlugin.log(NAME + ".initFont(): Can't create font: " + fontspec);
220 return;
221 }
222 initFont(getSourceViewer(), font);
223 }
224
225 ////////////////////////////////////////////////////////////////////////
226
227 /**
228 * @see org.eclipse.ui.texteditor.AbstractTextEditor#handlePreferenceStoreChanged(PropertyChangeEvent)
229 */
230 protected void handlePreferenceStoreChanged(PropertyChangeEvent event) {
231 String name = event.getProperty();
232 if (name.equals(IConstants.PREF_XML_BG)) {
233 initBackground();
234 } else if (name.equals(IConstants.PREF_XML_FONT)) {
235 initFont();
236 } else if (name.equals(IConstants.PREF_XML_TABWIDTH)) {
237 getSourceViewer().getTextWidget().setTabs(
238 getPreferenceStore().getInt(IConstants.PREF_XML_TABWIDTH));
239 } else if (name.equals(IConstants.PREF_XML_OUTLINE_ATTRIBUTES)) {
240 initOutlinePreferences();
241 }
242 fConfig.handlePreferenceStoreChanged(event);
243 }
244
245 /**
246 * @see org.eclipse.ui.editors.text.TextEditor#initializeEditor()
247 */
248 protected void initializeEditor() {
249 super.initializeEditor();
250 }
251
252 /**
253 * @see org.eclipse.ui.texteditor.AbstractTextEditor#editorContextMenuAboutToShow(IMenuManager)
254 */
255 protected void editorContextMenuAboutToShow(IMenuManager menu) {
256 super.editorContextMenuAboutToShow(menu);
257 SourceViewer viewer = getViewer();
258 IAction action;
259 //
260 action = getAction(FormatAction.ID);
261 action.setEnabled(viewer.canDoOperation(SourceViewer.FORMAT));
262 menu.appendToGroup(ITextEditorActionConstants.MB_ADDITIONS, action);
263 //
264 action = getAction(CompactFormatAction.ID);
265 action.setEnabled(viewer.canDoOperation(SourceViewer.FORMAT));
266 menu.appendToGroup(ITextEditorActionConstants.MB_ADDITIONS, action);
267 //
268 action = getAction(ValidateAction.ID);
269 action.setEnabled(true);
270 menu.appendToGroup(ITextEditorActionConstants.MB_ADDITIONS, action);
271 //
272 action = getAction(SelectTagAction.ID);
273 action.setEnabled(canDoSelectTag());
274 menu.appendToGroup(ITextEditorActionConstants.MB_ADDITIONS, action);
275 //
276 action = getAction(DeleteTagAction.ID);
277 action.setEnabled(canDoDeleteTag());
278 menu.appendToGroup(ITextEditorActionConstants.MB_ADDITIONS, action);
279 //
280 action = getAction(AddTagAction.ID);
281 action.setEnabled(canDoCreateTag());
282 menu.appendToGroup(ITextEditorActionConstants.MB_ADDITIONS, action);
283 }
284
285 /**
286 * @see org.eclipse.ui.texteditor.AbstractTextEditor#createActions()
287 */
288 protected void createActions() {
289 super.createActions();
290 Action action;
291 //
292 action = new FormatAction();
293 setAction(action.getId(), action);
294 action = new CompactFormatAction();
295 setAction(action.getId(), action);
296 action = new SelectTagAction();
297 setAction(action.getId(), action);
298 action = new DeleteTagAction();
299 setAction(action.getId(), action);
300 action = new AddTagAction();
301 setAction(action.getId(), action);
302 action = new ValidateAction();
303 setAction(action.getId(), action);
304 //
305 ThemeManager manager = EditorsPlugin.getThemeManager();
306 EditorsUtil.setActionActivationCode(
307 this,
308 FormatAction.ID,
309 manager.getString(ThemeManager.ACCELERATOR_FORMAT));
310 EditorsUtil.setActionActivationCode(
311 this,
312 CompactFormatAction.ID,
313 manager.getString(ThemeManager.ACCELERATOR_COMPACT_FORMAT));
314 EditorsUtil.setActionActivationCode(
315 this,
316 SelectTagAction.ID,
317 manager.getString(ThemeManager.ACCELERATOR_SELECT_TAG));
318 EditorsUtil.setActionActivationCode(
319 this,
320 DeleteTagAction.ID,
321 manager.getString(ThemeManager.ACCELERATOR_DELETE_TAG));
322 EditorsUtil.setActionActivationCode(
323 this,
324 AddTagAction.ID,
325 manager.getString(ThemeManager.ACCELERATOR_ADD_TAG));
326 EditorsUtil.setActionActivationCode(
327 this,
328 ValidateAction.ID,
329 manager.getString(ThemeManager.ACCELERATOR_VALIDATE));
330 }
331
332 void error(String message, Exception e) {
333 SourceViewer viewer = getViewer();
334 EditorsPlugin.error(message, e, getEditorInput());
335 EditorsUtil.beep(viewer);
336 return;
337 }
338
339 ////////////////////////////////////////////////////////////////////////
340
341 private boolean canDoSelectTag() {
342 return true;
343 }
344
345 private boolean canDoDeleteTag() {
346 Point p = getViewer().getSelectedRange();
347 return p.y != 0;
348 }
349
350 private boolean canDoCreateTag() {
351 return true;
352 }
353
354 ////////////////////////////////////////////////////////////////////////
355
356 class FormatAction extends Action {
357 public static final String ID = "com.port80.eclipse.xml.editors.XMLEditor.FormatAction";
358 public FormatAction() {
359 setEnabled(true);
360 setId(ID);
361 setText("&Format");
362 //setAccelerator(convertAccelerator("Ctrl+Shift+F"));
363 }
364 public void run() {
365 EditorsPlugin.deleteProblemMarkers(getEditorInput(), IConstants.FormatActionProblemMarker);
366 SourceViewer viewer = getViewer();
367 String original = EditorsUtil.getFormatOriginal(viewer);
368 //NOTE: Set to TextSelection.emptySelection() do not clear the selection!
369 viewer.setSelection(new TextSelection(viewer.getSelectedRange().x, 0));
370 viewer.setData(IConstants.KEY_COMPACT_FORMAT, Boolean.FALSE);
371 viewer.doOperation(SourceViewer.FORMAT);
372 EditorsUtil.verifyFormatted(XMLEditor.this, viewer, original);
373 // Update marker visuals.
374 createModel();
375 refreshOutlineView();
376 IDocument doc = viewer.getDocument();
377 IAnnotationModel model = viewer.getAnnotationModel();
378 model.disconnect(doc);
379 model.connect(doc);
380 }
381 }
382
383 /** For now CompactFormat is same as Format. */
384 class CompactFormatAction extends Action {
385 public static final String ID = "com.port80.eclipse.xml.editors.XMLEditor.CompactFormatAction";
386 public CompactFormatAction() {
387 setEnabled(true);
388 setId(ID);
389 setText("&Compact Format");
390 //setAccelerator(convertAccelerator("Ctrl+Shift+F"));
391 }
392 public void run() {
393 EditorsPlugin.deleteProblemMarkers(getEditorInput(), IConstants.FormatActionProblemMarker);
394 SourceViewer viewer = getViewer();
395 String original = EditorsUtil.getFormatOriginal(viewer);
396 viewer.setSelection(new TextSelection(viewer.getSelectedRange().x, 0));
397 viewer.setData(IConstants.KEY_COMPACT_FORMAT, Boolean.TRUE);
398 viewer.doOperation(SourceViewer.FORMAT);
399 EditorsUtil.verifyFormatted(XMLEditor.this, viewer, original);
400 // Update marker visuals.
401 createModel();
402 refreshOutlineView();
403 IDocument doc = viewer.getDocument();
404 IAnnotationModel model = viewer.getAnnotationModel();
405 model.disconnect(doc);
406 model.connect(doc);
407 }
408 }
409
410 ////////////////////////////////////////////////////////////////////////
411
412 class SelectTagAction extends Action {
413 public static final String ID = "com.port80.eclipse.xml.editors.XMLEditor.SelectTagAction";
414 public SelectTagAction() {
415 setEnabled(true);
416 setId(ID);
417 setText("&Select Tag");
418 //setAccelerator(convertAccelerator("Ctrl+Shift+F"));
419 }
420 public void run() {
421 EditorsPlugin.deleteProblemMarkers(getEditorInput(), IConstants.FormatActionProblemMarker);
422 SourceViewer viewer = getViewer();
423 Point selected = viewer.getSelectedRange();
424 XDocument xml = (XDocument) Util.parseXML(XMLEditor.this, viewer, false);
425 if (xml == null) {
426 viewer.getTextWidget().getShell().getDisplay().beep();
427 return;
428 }
429 IDocument doc = viewer.getDocument();
430 try {
431 int line = doc.getLineOfOffset(selected.x);
432 int column = selected.x - doc.getLineOffset(line);
433 Position p = Util.findElementRange(line + 1, column + 1, xml, doc);
434 if (p != null && p.offset >= 0) {
435 // Hack. Unfortunately, Xerces ignores whitespaces between the xmlDecl() and
436 // startDocument(). Locator is not updated to account for the ignored whitespaces.
437 // The start location of the root element is not correct.
438 // This is hack to skip the whitespaces before the document node.
439 int end = -1;
440 if (xml.getDoctype() != null) {
441 XDocumentType doctype = (XDocumentType) xml.getDoctype();
442 XMLLocator endloc = doctype.getEndLocation();
443 end =
444 doc.getLineOffset(endloc.getLineNumber() - 1)
445 + endloc.getColumnNumber()
446 - 1;
447 }
448 if (p.offset <= end)
449 findEndOfDocType(p, doc);
450 for (; p.length > 0; ++p.offset, --p.length) {
451 if (doc.getChar(p.offset) == '<')
452 break;
453 }
454 viewer.setSelection(new TextSelection(p.offset, p.length), true);
455 } else
456 viewer.getTextWidget().getShell().getDisplay().beep();
457 } catch (BadLocationException e) {
458 error("SelectTagAction.run(): Invalid offset", e);
459 }
460 }
461
462 private void findEndOfDocType(Position p, IDocument doc) {
463 // Start inside an DOCTYPE node, look for end of the doctype node first.
464 int offset = p.offset;
465 int length = p.length;
466 int c;
467 char[] pat = new char[] { '[', ']', '>' };
468 int n = 0;
469 try {
470 for (; length > 0; ++offset, --length) {
471 c = doc.getChar(offset);
472 if (c == '<') {
473 if (length > 1 && XMLChar.isName(doc.getChar(offset + 1)))
474 return;
475 } else if (c == pat[n]) {
476 ++n;
477 if (n == pat.length) {
478 p.offset = offset;
479 p.length = length;
480 return;
481 }
482 }
483 }
484 } catch (BadLocationException e) {
485 error("SelectTagAction.findEndOfDocType(): Bad location.", e);
486 return;
487 }
488 }
489
490 }
491
492 ////////////////////////////////////////////////////////////////////////
493
494 class ValidateAction extends Action {
495 public static final String ID = "com.port80.eclipse.xml.editors.XMLEditor.ValidateAction";
496 public ValidateAction() {
497 setEnabled(true);
498 setId(ID);
499 setText("&Validate");
500 }
501 public void run() {
502 EditorsPlugin.deleteProblemMarkers(getEditorInput(), IConstants.FormatActionProblemMarker);
503 SourceViewer viewer = getViewer();
504 Util.validateXML(XMLEditor.this, viewer, false);
505 IDocument doc = viewer.getDocument();
506 IAnnotationModel model = viewer.getAnnotationModel();
507 model.disconnect(doc);
508 model.connect(doc);
509 //
510 createModel(true);
511 refreshOutlineView();
512 }
513 }
514
515 ////////////////////////////////////////////////////////////////////////
516
517 class DeleteTagAction extends Action {
518 public static final String ID = "com.port80.eclipse.xml.editors.XMLEditor.DeleteTagAction";
519 public DeleteTagAction() {
520 setEnabled(true);
521 setId(ID);
522 setText("&Delete Tag");
523 //setAccelerator(convertAccelerator("Ctrl+Shift+F"));
524 }
525
526 public void run() {
527 EditorsPlugin.deleteProblemMarkers(getEditorInput(), IConstants.FormatActionProblemMarker);
528 SourceViewer viewer = getViewer();
529 Point selected = viewer.getSelectedRange();
530 if (selected == null || selected.x < 0 || selected.y == 0) {
531 error("Please select a tag", null);
532 return;
533 }
534 Document xmldoc = Util.parseXML(XMLEditor.this, viewer, false);
535 if (xmldoc == null) {
536 EditorsUtil.beep(viewer);
537 return;
538 }
539 IDocument doc = viewer.getDocument();
540 try {
541 int line = doc.getLineOfOffset(selected.x);
542 int column = selected.x - doc.getLineOffset(line);
543 Node node = Util.findNode(line + 1, column + 1, xmldoc, doc);
544 if (node == null) {
545 error(
546 "Cannot find tag at: line=" + (line + 1) + ", column=" + (column + 1),
547 null);
548 return;
549 }
550 removeTag(selected, node, doc);
551 } catch (BadLocationException e) {
552 error("Invalid offset", e);
553 }
554 }
555
556 private void removeTag(Point selected, Node node, IDocument doc) {
557 SourceViewer viewer = getViewer();
558 IXRange range = (IXRange) node;
559 int start = selected.x;
560 int len = selected.y;
561 int newstart = start;
562 int newlen = 0;
563 try {
564 if (node instanceof XElementNS) {
565 newstart = EditorsUtil.forwardCloseBracket(doc, start, start + len) + 1;
566 newlen = EditorsUtil.backwardOpenBracket(doc, start, start + len) - newstart;
567 } else if (node instanceof XComment) {
568 // <!--...-->
569 newstart = start + 4;
570 newlen = len - 7;
571 if (doc.getChar(newstart) == ' ') {
572 ++newstart;
573 --newlen;
574 }
575 if (doc.getChar(newstart + newlen - 1) == ' ')
576 --newlen;
577 } else if (node instanceof XProcessingInstruction) {
578 // <? ... ?>
579 newstart = EditorsUtil.forwardSpace(doc, start, start + len) + 1;
580 newlen = len - 2 - (newstart - start);
581 if (doc.getChar(newstart + newlen - 1) == ' ')
582 --newlen;
583 }
584 } catch (BadLocationException e) {
585 error("Parser error, bad location", e);
586 return;
587 }
588 if (newlen < 0) {
589 EditorsUtil.beep(viewer);
590 return;
591 }
592 try {
593 Position p = new Position(start, newlen);
594 if (node.hasChildNodes()) {
595 range = (IXRange) node.getFirstChild();
596 int s =
597 doc.getLineOffset(range.getStartLine() - 1)
598 + range.getStartColumn()
599 - 1;
600 int e = doc.getLineOffset(range.getEndLine() - 1) + range.getEndColumn() - 1;
601 range = Util.getEndRange(node, doc);
602 if (range != null)
603 e =
604 doc.getLineOffset(range.getStartLine() - 1)
605 + range.getStartColumn()
606 - 1;
607 p.setOffset(s);
608 p.setLength(e - s);
609 doc.addPosition(p);
610 }
611 doc.replace(newstart + newlen, start + len - newstart - newlen, "");
612 doc.replace(start, newstart - start, "");
613 Point e = EditorsUtil.removeBlankLineAt(doc, start + newlen);
614 Point s = EditorsUtil.removeBlankLineAt(doc, start);
615 if (node.hasChildNodes()) {
616 viewer.setSelectedRange(p.getOffset(), p.getLength());
617 doc.removePosition(p);
618 } else
619 viewer.setSelectedRange(s.x, e.x - s.x - s.y);
620 } catch (BadLocationException e) {
621 error("Bad location", e);
622 return;
623 }
624 }
625 }
626
627 ////////////////////////////////////////////////////////////////////////
628
629 class AddTagAction extends Action {
630 public static final String ID = "com.port80.eclipse.xml.editors.XMLEditor.AddTagAction";
631 public AddTagAction() {
632 setEnabled(true);
633 setId(ID);
634 setText("&Add Tag");
635 //setAccelerator(convertAccelerator("Ctrl+Shift+F"));
636 }
637 public void run() {
638 EditorsPlugin.deleteProblemMarkers(getEditorInput(), IConstants.FormatActionProblemMarker);
639 final SourceViewer viewer = getViewer();
640 Point selected = viewer.getSelectedRange();
641 if (selected == null || selected.x < 0) {
642 EditorsUtil.beep(viewer);
643 return;
644 }
645 final XDocument xmldoc = (XDocument) Util.parseXML(XMLEditor.this, viewer, false);
646 if (xmldoc == null) {
647 EditorsUtil.beep(viewer);
648 return;
649 }
650 IDocument doc = viewer.getDocument();
651 //
652 // Ask for tag name.
653 InputDialog a =
654 new InputDialog(
655 viewer.getTextWidget().getShell(),
656 "Create Tag",
657 "Enter Tag Name (also accept \"-\" for comment)",
658 "",
659 new IInputValidator() {
660 public String isValid(String newText) {
661 String ret = checkTagName(newText, xmldoc);
662 if (ret != null)
663 EditorsUtil.beep(viewer);
664 return ret;
665 }
666 });
667 if (a.open() != InputDialog.OK)
668 return;
669 String tagname = a.getValue();
670 if (tagname == null || tagname.length() == 0)
671 return;
672 addTag(tagname, selected, null, doc);
673 }
674
675 String checkTagName(String text, XDocument xmldoc) {
676 final String errmsg = "Invalid tag name. expect letters, digits and -, ?, !";
677 //
678 if (text.length() == 0)
679 return null;
680 char c = text.charAt(0);
681 if (c == '!' || c == '?')
682 text = text.substring(1);
683 if (text.length() == 0)
684 return null;
685 if ("--".startsWith(text))
686 return null;
687 if (XDocument.isXMLName(text))
688 return null;
689 return errmsg;
690 }
691
692 private void addTag(String tagname, Point selected, Node node, IDocument doc) {
693 ISourceViewer viewer = XMLEditor.this.getViewer();
694 int start = selected.x;
695 int len = selected.y;
696 String endtag = null;
697 if (tagname.equals("!--") || "--".startsWith(tagname)) {
698 tagname = "<!-- ";
699 endtag = " -->";
700 } else if (tagname.startsWith("?")) {
701 tagname = "<" + tagname + " ";
702 endtag = " ?>";
703 } else if (tagname.equals("![CDATA[")) {
704 tagname = "<" + tagname;
705 endtag = "]]>";
706 } else {
707 endtag = "</" + tagname + ">";
708 tagname = "<" + tagname + ">";
709 }
710 try {
711 doc.replace(start + len, 0, endtag);
712 doc.replace(start, 0, tagname);
713 viewer.setSelectedRange(start, len + tagname.length() + endtag.length());
714 } catch (BadLocationException e) {
715 error("Parser error, bad location", e);
716 return;
717 }
718 }
719 }
720
721 ////////////////////////////////////////////////////////////////////////
722
723 }