KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > eclipse > pde > internal > ui > editor > contentassist > XMLCompletionProposal


1 /*******************************************************************************
2  * Copyright (c) 2006, 2007 IBM Corporation and others.
3  * All rights reserved. This program and the accompanying materials
4  * are made available under the terms of the Eclipse Public License v1.0
5  * which accompanies this distribution, and is available at
6  * http://www.eclipse.org/legal/epl-v10.html
7  *
8  * Contributors:
9  * IBM Corporation - initial API and implementation
10  *******************************************************************************/

11
12 package org.eclipse.pde.internal.ui.editor.contentassist;
13
14 import java.util.HashSet JavaDoc;
15 import java.util.Stack JavaDoc;
16
17 import org.eclipse.core.runtime.CoreException;
18 import org.eclipse.core.runtime.IProgressMonitor;
19 import org.eclipse.jface.text.AbstractReusableInformationControlCreator;
20 import org.eclipse.jface.text.BadLocationException;
21 import org.eclipse.jface.text.IDocument;
22 import org.eclipse.jface.text.IInformationControl;
23 import org.eclipse.jface.text.IInformationControlCreator;
24 import org.eclipse.jface.text.ITextSelection;
25 import org.eclipse.jface.text.TextUtilities;
26 import org.eclipse.jface.text.contentassist.ICompletionProposal;
27 import org.eclipse.jface.text.contentassist.ICompletionProposalExtension3;
28 import org.eclipse.jface.text.contentassist.ICompletionProposalExtension5;
29 import org.eclipse.jface.text.contentassist.IContextInformation;
30 import org.eclipse.pde.core.IBaseModel;
31 import org.eclipse.pde.core.plugin.IPluginAttribute;
32 import org.eclipse.pde.core.plugin.IPluginBase;
33 import org.eclipse.pde.core.plugin.IPluginElement;
34 import org.eclipse.pde.core.plugin.IPluginExtension;
35 import org.eclipse.pde.core.plugin.IPluginModelBase;
36 import org.eclipse.pde.core.plugin.IPluginObject;
37 import org.eclipse.pde.core.plugin.IPluginParent;
38 import org.eclipse.pde.internal.core.ischema.ISchemaAttribute;
39 import org.eclipse.pde.internal.core.ischema.ISchemaElement;
40 import org.eclipse.pde.internal.core.ischema.ISchemaObject;
41 import org.eclipse.pde.internal.core.text.AbstractEditingModel;
42 import org.eclipse.pde.internal.core.text.IDocumentAttribute;
43 import org.eclipse.pde.internal.core.text.IDocumentNode;
44 import org.eclipse.pde.internal.core.text.IDocumentRange;
45 import org.eclipse.pde.internal.core.text.IReconcilingParticipant;
46 import org.eclipse.pde.internal.core.text.plugin.PluginAttribute;
47 import org.eclipse.pde.internal.ui.PDEPlugin;
48 import org.eclipse.pde.internal.ui.PDEUIMessages;
49 import org.eclipse.pde.internal.ui.editor.PDESourcePage;
50 import org.eclipse.pde.internal.ui.editor.contentassist.display.BrowserInformationControl;
51 import org.eclipse.pde.internal.ui.editor.text.HTMLPrinter;
52 import org.eclipse.pde.internal.ui.editor.text.XMLUtil;
53 import org.eclipse.pde.internal.ui.util.TextUtil;
54 import org.eclipse.swt.SWT;
55 import org.eclipse.swt.graphics.Image;
56 import org.eclipse.swt.graphics.Point;
57 import org.eclipse.swt.widgets.Shell;
58
59 public class XMLCompletionProposal implements ICompletionProposal, ICompletionProposalExtension5, ICompletionProposalExtension3 {
60     
61     private static final String JavaDoc F_DEF_ATTR_INDENT = " "; //$NON-NLS-1$
62

63     private ISchemaObject fSchemaObject;
64     private IDocumentRange fRange;
65     private int fOffset;
66     private int fLen;
67     private int fSelOffset;
68     private int fSelLen;
69     private XMLContentAssistProcessor fProcessor;
70     private String JavaDoc fAddInfo;
71     private IInformationControlCreator fCreator;
72     
73     private IPluginParent fPluginParent;
74     private ISchemaElement fSchemaElement;
75     
76     public XMLCompletionProposal(IDocumentRange node, ISchemaObject object, int offset, XMLContentAssistProcessor processor) {
77         fLen = -1;
78         fSelOffset = -1;
79         fSelLen = 0;
80         fRange = node;
81         fSchemaObject = object;
82         fOffset = offset;
83         fProcessor = processor;
84     }
85
86     public void apply(IDocument document) {
87         ITextSelection sel = fProcessor.getCurrentSelection();
88         if (sel == null) {
89             return;
90         }
91         fLen = sel.getLength() + sel.getOffset() - fOffset;
92         String JavaDoc delim = TextUtilities.getDefaultLineDelimiter(document);
93         StringBuffer JavaDoc documentInsertBuffer = new StringBuffer JavaDoc();
94         boolean doInternalWork = false;
95         // Generate the text to apply depending on the proposal type
96
if (fSchemaObject instanceof ISchemaAttribute) {
97             applyAttribute(documentInsertBuffer);
98         } else if (fSchemaObject instanceof ISchemaElement) {
99             applyElement(getIndent(document, fOffset), delim, documentInsertBuffer);
100             doInternalWork = true;
101         } else if (fSchemaObject instanceof VirtualSchemaObject) {
102             doInternalWork = applyVirtual(document, sel, delim, documentInsertBuffer, doInternalWork);
103         }
104         // Check if there is anything to apply
105
if (documentInsertBuffer.length() == 0) {
106             return;
107         }
108         // Apply the proposal to the document
109
try {
110             document.replace(fOffset, fLen, documentInsertBuffer.toString());
111         } catch (BadLocationException e) {
112             PDEPlugin.log(e);
113         }
114         // Update the model if necessary
115
if (doInternalWork) {
116             modifyModel(document);
117         }
118     }
119
120     /**
121      * @param document
122      * @param sel
123      * @param delim
124      * @param documentInsertBuffer
125      * @param doInternalWork
126      * @return
127      */

128     private boolean applyVirtual(IDocument document, ITextSelection sel, String JavaDoc delim, StringBuffer JavaDoc documentInsertBuffer, boolean doInternalWork) {
129         int type = ((VirtualSchemaObject)fSchemaObject).getVType();
130         switch (type) {
131         case XMLContentAssistProcessor.F_ATTRIBUTE:
132             applyAttribute(documentInsertBuffer);
133             break;
134         case XMLContentAssistProcessor.F_CLOSE_TAG:
135             fOffset = sel.getOffset();
136             fLen = 0;
137             documentInsertBuffer.append(" />"); //$NON-NLS-1$
138
break;
139         case XMLContentAssistProcessor.F_EXTENSION:
140             applyExtension(document, delim, documentInsertBuffer);
141             break;
142         case XMLContentAssistProcessor.F_EXTENSION_POINT:
143             applyExtensionPoint(documentInsertBuffer);
144             break;
145         case XMLContentAssistProcessor.F_EXTENSION_POINT_AND_VALUE:
146             doInternalWork = true; // we will want to add required child nodes/attributes
147
applyExtensionFullPoint(document, delim, documentInsertBuffer);
148             break;
149         case XMLContentAssistProcessor.F_EXTENSION_ATTRIBUTE_POINT_VALUE:
150             doInternalWork = true; // we will want to add required child nodes/attributes
151
case XMLContentAssistProcessor.F_ATTRIBUTE_VALUE:
152             applyAttributeValue(document, documentInsertBuffer);
153             break;
154         }
155         return doInternalWork;
156     }
157
158     /**
159      * @param document
160      * @param documentInsertBuffer
161      */

162     private void applyAttributeValue(IDocument document, StringBuffer JavaDoc documentInsertBuffer) {
163         if (fRange instanceof IDocumentAttribute) {
164             fOffset = ((IDocumentAttribute)fRange).getValueOffset();
165             String JavaDoc value = fSchemaObject.getName();
166             try {
167                 // add indentation
168
int off = fOffset;
169                 int docLen = document.getLength();
170                 fLen = 0;
171                 while (off < docLen) {
172                     char c = document.getChar(off++);
173                     if (c == '"')
174                         break;
175                     fLen += 1;
176                 }
177             } catch (BadLocationException e) {
178             }
179             documentInsertBuffer.append(value);
180             fSelOffset = fOffset + value.length();
181         }
182     }
183
184     /**
185      * @param documentInsertBuffer
186      */

187     private void applyExtensionPoint(StringBuffer JavaDoc documentInsertBuffer) {
188         String JavaDoc id = "id"; //$NON-NLS-1$
189
documentInsertBuffer.append("<extension-point id=\""); //$NON-NLS-1$
190
fSelOffset = fOffset + documentInsertBuffer.length();
191         fSelLen = id.length();
192         documentInsertBuffer.append(id);
193         documentInsertBuffer.append("\" name=\"name\" />"); //$NON-NLS-1$
194
}
195
196     /**
197      * @param document
198      * @param delim
199      * @param documentInsertBuffer
200      */

201     private void applyExtension(IDocument document, String JavaDoc delim, StringBuffer JavaDoc documentInsertBuffer) {
202         documentInsertBuffer.append("<extension"); //$NON-NLS-1$
203
documentInsertBuffer.append(delim);
204         String JavaDoc indent = getIndent(document, fOffset);
205         documentInsertBuffer.append(indent);
206         documentInsertBuffer.append(F_DEF_ATTR_INDENT);
207         documentInsertBuffer.append("point=\"\">"); //$NON-NLS-1$
208
fSelOffset = fOffset + documentInsertBuffer.length() - 2; // position rigth inside new point="" attribute
209
documentInsertBuffer.append(delim);
210         documentInsertBuffer.append(indent);
211         documentInsertBuffer.append("</extension>"); //$NON-NLS-1$
212
}
213
214     /**
215      * @param document
216      * @param delim
217      * @param documentInsertBuffer
218      */

219     private void applyExtensionFullPoint(IDocument document, String JavaDoc delim,
220             StringBuffer JavaDoc documentInsertBuffer) {
221         
222         String JavaDoc pointID = fSchemaObject.getName();
223         String JavaDoc indent = getIndent(document, fOffset);
224         // Add extension mark-up to the buffer right up until the point
225
// attribute value
226
documentInsertBuffer.append('<');
227         documentInsertBuffer.append("extension"); //$NON-NLS-1$
228
documentInsertBuffer.append(delim);
229         documentInsertBuffer.append(indent);
230         documentInsertBuffer.append(F_DEF_ATTR_INDENT);
231         documentInsertBuffer.append("point"); //$NON-NLS-1$
232
documentInsertBuffer.append('=');
233         documentInsertBuffer.append('"');
234         // Calculate the offset for the start of the selection
235
// We want to select the point attribute value in between the quotes
236
// fOffset is the point where content assist was first invoked
237
fSelOffset = fOffset + documentInsertBuffer.length();
238         // Calculate the selection length
239
fSelLen = pointID.length();
240         // Add extension mark-up to the buffer including the point attribute
241
// value and beyond
242
documentInsertBuffer.append(pointID);
243         documentInsertBuffer.append('"');
244         documentInsertBuffer.append('>');
245         documentInsertBuffer.append(delim);
246         documentInsertBuffer.append(indent);
247         documentInsertBuffer.append('<');
248         documentInsertBuffer.append('/');
249         documentInsertBuffer.append("extension"); //$NON-NLS-1$
250
documentInsertBuffer.append('>');
251     }
252     
253     /**
254      * @param document
255      * @param delim
256      * @param sb
257      */

258     private void applyElement(String JavaDoc indent, String JavaDoc delim, StringBuffer JavaDoc documentInsertBuffer) {
259         documentInsertBuffer.append('<');
260         documentInsertBuffer.append(((ISchemaElement)fSchemaObject).getName());
261         documentInsertBuffer.append('>');
262         documentInsertBuffer.append(delim);
263         documentInsertBuffer.append(indent);
264         documentInsertBuffer.append('<');
265         documentInsertBuffer.append('/');
266         documentInsertBuffer.append(((ISchemaElement)fSchemaObject).getName());
267         documentInsertBuffer.append('>');
268     }
269
270     /**
271      * @param sb
272      */

273     private void applyAttribute(StringBuffer JavaDoc documentInsertBuffer) {
274         if (fRange == null) {
275             // Model is broken
276
// Manually adjust offsets
277
fLen -= 1;
278             fOffset += 1;
279         }
280         String JavaDoc attName = fSchemaObject.getName();
281         documentInsertBuffer.append(attName);
282         documentInsertBuffer.append("=\""); //$NON-NLS-1$
283
fSelOffset = fOffset + documentInsertBuffer.length();
284         String JavaDoc value = attName; //$NON-NLS-1$
285
if (fSchemaObject instanceof ISchemaAttribute) {
286             value = XMLInsertionComputer.generateAttributeValue(
287                     (ISchemaAttribute) fSchemaObject, fProcessor.getModel(),
288                     attName);
289         }
290         documentInsertBuffer.append(value);
291         fSelLen = value.length();
292         documentInsertBuffer.append('"');
293     }
294     
295     private void modifyModel(IDocument document) {
296         // TODO requires
297
// - refactoring
298
// - better grouping of cases (if statements)
299
IBaseModel model = fProcessor.getModel();
300         if (model instanceof IReconcilingParticipant)
301             ((IReconcilingParticipant)model).reconciled(document);
302         
303         if (model instanceof IPluginModelBase) {
304             IPluginBase base = ((IPluginModelBase)model).getPluginBase();
305             
306             fPluginParent = null;
307             fSchemaElement = null;
308             
309             if (fSchemaObject instanceof VirtualSchemaObject) {
310                 switch (((VirtualSchemaObject)fSchemaObject).getVType()) {
311                 case XMLContentAssistProcessor.F_EXTENSION_ATTRIBUTE_POINT_VALUE:
312                     if (!(fRange instanceof IDocumentAttribute))
313                         break;
314                     int offset = ((IDocumentAttribute)fRange).getEnclosingElement().getOffset();
315                     IPluginExtension[] extensions = base.getExtensions();
316                     for (int i = 0; i < extensions.length; i++) {
317                         if (((IDocumentNode)extensions[i]).getOffset() == offset) {
318                             if (extensions[i].getChildCount() != 0)
319                                 break; // don't modify existing extensions
320
fPluginParent = extensions[i];
321                             fSchemaElement = XMLUtil.getSchemaElement(
322                                     (IDocumentNode)extensions[i],
323                                     extensions[i].getPoint());
324                             break;
325                         }
326                     }
327                     break;
328                 case XMLContentAssistProcessor.F_EXTENSION_POINT_AND_VALUE:
329                     findExtensionVirtualPointValue(base);
330                     break;
331                 }
332             } else if (fRange instanceof IDocumentNode && base instanceof IDocumentNode) {
333                 Stack JavaDoc s = new Stack JavaDoc();
334                 IDocumentNode node = (IDocumentNode)fRange;
335                 IDocumentNode newSearch = (IDocumentNode)base;
336                 // traverse up old model, pushing all nodes onto the stack along the way
337
while (node != null && !(node instanceof IPluginBase)) {
338                     s.push(node);
339                     node = node.getParentNode();
340                 }
341                 
342                 // traverse down new model to find new node, using stack as a guideline
343
while (!s.isEmpty()) {
344                     node = (IDocumentNode)s.pop();
345                     int nodeIndex = 0;
346                     while ((node = node.getPreviousSibling()) != null)
347                         nodeIndex += 1;
348                     newSearch = newSearch.getChildAt(nodeIndex);
349                 }
350                 if (newSearch != null) {
351                     IDocumentNode[] children = newSearch.getChildNodes();
352                     for (int i = 0; i < children.length; i++) {
353                         if (children[i].getOffset() == fOffset &&
354                                 children[i] instanceof IPluginElement) {
355                             fPluginParent = (IPluginElement)children[i];
356                             fSchemaElement = (ISchemaElement)fSchemaObject;
357                             break;
358                         }
359                     }
360                 }
361             }
362             
363             if (fPluginParent != null && fSchemaElement != null) {
364                 XMLInsertionComputer.computeInsertion(fSchemaElement, fPluginParent);
365                 fProcessor.flushDocument();
366                 if (model instanceof AbstractEditingModel) {
367                     try {
368                         ((AbstractEditingModel)model).adjustOffsets(document);
369                     } catch (CoreException e) {
370                     }
371                     setSelectionOffsets(document, fSchemaElement, fPluginParent);
372                 }
373             }
374         }
375     }
376     
377     /**
378      * Assumption: Model already reconciled by caller
379      * @param base
380      */

381     private void findExtensionVirtualPointValue(IPluginBase base) {
382         
383         IDocumentRange range = null;
384         PDESourcePage page = fProcessor.getSourcePage();
385         // Ensure page is defined
386
if (page == null) {
387             return;
388         }
389         // When we inserted the extension element and extension point attribute
390
// name and value, we selected the point value
391
// Find the corresponding range in order to add child elements to
392
// the proper extension.
393
range = page.getRangeElement(fOffset, true);
394         // Ensure the range is an attribute
395
if ((range == null) ||
396                 (range instanceof IDocumentNode) == false) {
397             return;
398         }
399         // Get the offset of the extension element
400
int targetOffset = ((IDocumentNode)range).getOffset();
401         // Search this plug-ins extensions for the proper one
402
IPluginExtension[] extensions = base.getExtensions();
403         for (int i = 0; i < extensions.length; i++) {
404             // Get the offset of the current extension
405
int extensionOffset = ((IDocumentNode)extensions[i]).getOffset();
406             // If the offsets match we foudn the extension element
407
// Note: The extension element should have no children
408
if ((extensionOffset == targetOffset) &&
409                     (extensions[i].getChildCount() == 0)) {
410                 fPluginParent = extensions[i];
411                 // Get the corresponding schema element
412
fSchemaElement = XMLUtil.getSchemaElement(
413                         (IDocumentNode)extensions[i],
414                         extensions[i].getPoint());
415                 break;
416             }
417         }
418     }
419
420     private void setSelectionOffsets(IDocument document, ISchemaElement schemaElement, IPluginParent pluginParent) {
421         if (pluginParent instanceof IPluginExtension) {
422             String JavaDoc point = ((IPluginExtension)pluginParent).getPoint();
423             IPluginObject[] children = ((IPluginExtension)pluginParent).getChildren();
424             if (children != null && children.length > 0 && children[0] instanceof IPluginParent) {
425                 pluginParent = (IPluginParent)children[0];
426                 schemaElement = XMLUtil.getSchemaElement((IDocumentNode)pluginParent, point);
427             }
428         }
429         
430         if (pluginParent instanceof IPluginElement) {
431             int offset = ((IDocumentNode)pluginParent).getOffset();
432             int len = ((IDocumentNode)pluginParent).getLength();
433             String JavaDoc value = null;
434             try {
435                 value = document.get(offset, len);
436             } catch (BadLocationException e) {
437             }
438             if (((IPluginElement)pluginParent).getAttributeCount() > 0) {
439                 // Select value of first required attribute
440
IPluginAttribute att = ((IPluginElement)pluginParent).getAttributes()[0];
441                 if (att instanceof PluginAttribute) {
442                     fSelOffset = ((PluginAttribute)att).getValueOffset();
443                     fSelLen = ((PluginAttribute)att).getValueLength();
444                 }
445             } else if (XMLInsertionComputer.hasOptionalChildren(schemaElement, false, new HashSet JavaDoc()) && value != null) {
446                 int ind = value.indexOf('>');
447                 if (ind > 0) {
448                     fSelOffset = offset + ind + 1;
449                     fSelLen = 0;
450                 }
451             } else if (XMLInsertionComputer.hasOptionalAttributes(schemaElement) && value != null) {
452                 int ind = value.indexOf('>');
453                 if (ind != -1) {
454                     fSelOffset = offset + ind;
455                     fSelLen = 0;
456                 }
457             } else {
458                 // position caret after element
459
fSelOffset = offset + len;
460                 fSelLen = 0;
461             }
462         }
463     }
464     
465     private String JavaDoc getIndent(IDocument document, int offset) {
466         StringBuffer JavaDoc indBuff = new StringBuffer JavaDoc();
467         try {
468             // add indentation
469
int line = document.getLineOfOffset(offset);
470             int lineOffset = document.getLineOffset(line);
471             int indent = offset - lineOffset;
472             char[] indentChars = document.get(lineOffset, indent).toCharArray();
473             // for every tab append a tab, for anything else append a space
474
for (int i = 0; i < indentChars.length; i++)
475                 indBuff.append(indentChars[i] == '\t' ? '\t' : ' ');
476         } catch (BadLocationException e) {
477         }
478         return indBuff.toString();
479     }
480     
481     public String JavaDoc getAdditionalProposalInfo() {
482         if (fAddInfo == null) {
483             if (fSchemaObject == null)
484                 return null;
485             StringBuffer JavaDoc sb = new StringBuffer JavaDoc();
486             HTMLPrinter.insertPageProlog(sb, 0, TextUtil.getJavaDocStyleSheerURL());
487             String JavaDoc desc = null;
488             if (fSchemaObject == null)
489                 desc = PDEUIMessages.BaseWizardSelectionPage_noDesc;
490             else {
491                 desc = fSchemaObject.getDescription();
492                 if (desc == null || desc.trim().length() == 0)
493                     desc = PDEUIMessages.BaseWizardSelectionPage_noDesc;
494             }
495             sb.append(desc);
496             HTMLPrinter.addPageEpilog(sb);
497             fAddInfo = sb.toString();
498         }
499         return fAddInfo;
500     }
501
502     public IContextInformation getContextInformation() {
503         return null;
504     }
505
506     public String JavaDoc getDisplayString() {
507         if (fSchemaObject instanceof VirtualSchemaObject) {
508             switch (((VirtualSchemaObject)fSchemaObject).getVType()) {
509             case XMLContentAssistProcessor.F_CLOSE_TAG:
510                 return "... />"; //$NON-NLS-1$
511
case XMLContentAssistProcessor.F_EXTENSION_POINT_AND_VALUE:
512             case XMLContentAssistProcessor.F_EXTENSION_ATTRIBUTE_POINT_VALUE:
513             case XMLContentAssistProcessor.F_ATTRIBUTE_VALUE:
514                 return fSchemaObject.getName();
515             }
516         }
517         if (fSchemaObject instanceof ISchemaAttribute)
518             return fSchemaObject.getName();
519         if (fSchemaObject != null)
520             return fSchemaObject.getName();
521         if (fRange instanceof IDocumentNode)
522             return "...> </" + ((IDocumentNode)fRange).getXMLTagName() + ">"; //$NON-NLS-1$ //$NON-NLS-2$
523
return null;
524     }
525
526     public Image getImage() {
527         if (fSchemaObject instanceof VirtualSchemaObject)
528             return fProcessor.getImage(((VirtualSchemaObject)fSchemaObject).getVType());
529         if (fSchemaObject instanceof ISchemaAttribute)
530             return fProcessor.getImage(XMLContentAssistProcessor.F_ATTRIBUTE);
531         if (fSchemaObject instanceof ISchemaElement || fSchemaObject == null)
532             return fProcessor.getImage(XMLContentAssistProcessor.F_ELEMENT);
533         return null;
534     }
535
536     public Point getSelection(IDocument document) {
537         if (fSelOffset == -1)
538             return null;
539         return new Point(fSelOffset, fSelLen);
540     }
541
542     public Object JavaDoc getAdditionalProposalInfo(IProgressMonitor monitor) {
543         return getAdditionalProposalInfo();
544     }
545
546     public IInformationControlCreator getInformationControlCreator() {
547         if (!BrowserInformationControl.isAvailable(null))
548             return null;
549         
550         if (fCreator == null) {
551             fCreator = new AbstractReusableInformationControlCreator() {
552                 public IInformationControl doCreateInformationControl(Shell parent) {
553                     return new BrowserInformationControl(parent, SWT.NO_TRIM | SWT.TOOL, SWT.NONE);
554                 }
555             };
556         }
557         return fCreator;
558     }
559
560     public int getPrefixCompletionStart(IDocument document, int completionOffset) {
561         return 0;
562     }
563
564     public CharSequence JavaDoc getPrefixCompletionText(IDocument document, int completionOffset) {
565         return null;
566     }
567     
568 }
569
Popular Tags