Docjar: A Java Source and Docuemnt Enginecom.*    java.*    javax.*    org.*    all    new    plug-in

Quick Search    Search Deep

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 }