KickJava   Java API By Example, From Geeks To Geeks.

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


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.ArrayList JavaDoc;
15 import java.util.HashSet JavaDoc;
16 import java.util.Iterator JavaDoc;
17 import java.util.regex.Pattern JavaDoc;
18
19 import org.eclipse.core.resources.IResource;
20 import org.eclipse.jdt.core.search.IJavaSearchConstants;
21 import org.eclipse.jface.text.BadLocationException;
22 import org.eclipse.jface.text.IDocument;
23 import org.eclipse.jface.text.ITextSelection;
24 import org.eclipse.jface.text.ITextViewer;
25 import org.eclipse.jface.text.contentassist.ContentAssistEvent;
26 import org.eclipse.jface.text.contentassist.ICompletionListener;
27 import org.eclipse.jface.text.contentassist.ICompletionProposal;
28 import org.eclipse.jface.text.contentassist.IContentAssistProcessor;
29 import org.eclipse.jface.viewers.ISelection;
30 import org.eclipse.pde.core.IBaseModel;
31 import org.eclipse.pde.core.IIdentifiable;
32 import org.eclipse.pde.core.plugin.IPluginBase;
33 import org.eclipse.pde.core.plugin.IPluginExtension;
34 import org.eclipse.pde.core.plugin.IPluginExtensionPoint;
35 import org.eclipse.pde.core.plugin.IPluginModelBase;
36 import org.eclipse.pde.core.plugin.IPluginObject;
37 import org.eclipse.pde.core.plugin.PluginRegistry;
38 import org.eclipse.pde.internal.core.ischema.IMetaAttribute;
39 import org.eclipse.pde.internal.core.ischema.ISchemaAttribute;
40 import org.eclipse.pde.internal.core.ischema.ISchemaComplexType;
41 import org.eclipse.pde.internal.core.ischema.ISchemaCompositor;
42 import org.eclipse.pde.internal.core.ischema.ISchemaElement;
43 import org.eclipse.pde.internal.core.ischema.ISchemaObject;
44 import org.eclipse.pde.internal.core.ischema.ISchemaRestriction;
45 import org.eclipse.pde.internal.core.ischema.ISchemaSimpleType;
46 import org.eclipse.pde.internal.core.text.AbstractEditingModel;
47 import org.eclipse.pde.internal.core.text.IDocumentAttribute;
48 import org.eclipse.pde.internal.core.text.IDocumentNode;
49 import org.eclipse.pde.internal.core.text.IDocumentRange;
50 import org.eclipse.pde.internal.core.text.IDocumentTextNode;
51 import org.eclipse.pde.internal.core.text.IReconcilingParticipant;
52 import org.eclipse.pde.internal.core.text.plugin.PluginModelBase;
53 import org.eclipse.pde.internal.core.util.IdUtil;
54 import org.eclipse.pde.internal.ui.PDEPluginImages;
55 import org.eclipse.pde.internal.ui.PDEUIMessages;
56 import org.eclipse.pde.internal.ui.editor.PDEFormEditor;
57 import org.eclipse.pde.internal.ui.editor.PDESourcePage;
58 import org.eclipse.pde.internal.ui.editor.text.XMLUtil;
59 import org.eclipse.swt.graphics.Image;
60 import org.eclipse.ui.forms.editor.FormEditor;
61
62 public class XMLContentAssistProcessor extends TypePackageCompletionProcessor implements IContentAssistProcessor, ICompletionListener {
63
64     protected boolean fAssistSessionStarted;
65     
66     // Specific assist types
67

68     protected static final int F_INFER_BY_OBJECT = -1;
69
70     protected static final int F_EXTENSION_POINT = 0;
71
72     protected static final int F_EXTENSION = 1;
73
74     protected static final int F_ELEMENT = 2;
75
76     protected static final int F_ATTRIBUTE = 3;
77
78     protected static final int F_CLOSE_TAG = 4;
79
80     protected static final int F_ATTRIBUTE_VALUE = 5;
81
82     protected static final int F_EXTENSION_ATTRIBUTE_POINT_VALUE = 6;
83
84     protected static final int F_EXTENSION_POINT_AND_VALUE = 7;
85     
86     protected static final int F_TOTAL_TYPES = 8;
87     
88     // proposal generation type
89
private static final int
90         F_NO_ASSIST = 0,
91         F_ADD_ATTRIB = 1,
92         F_ADD_CHILD = 2,
93         F_OPEN_TAG = 3;
94     
95     private static final ArrayList JavaDoc F_V_BOOLS = new ArrayList JavaDoc();
96     static {
97         F_V_BOOLS.add(new VirtualSchemaObject("true", null, F_ATTRIBUTE_VALUE)); //$NON-NLS-1$
98
F_V_BOOLS.add(new VirtualSchemaObject("false", null, F_ATTRIBUTE_VALUE)); //$NON-NLS-1$
99
}
100     
101     private static final String JavaDoc F_STR_EXT_PT = "extension-point"; //$NON-NLS-1$
102
private static final String JavaDoc F_STR_EXT = "extension"; //$NON-NLS-1$
103

104     private PDESourcePage fSourcePage;
105     private final Image[] fImages = new Image[F_TOTAL_TYPES];
106     // TODO add a listener to add/remove extension points as they are added/removed from working models
107
private IDocumentRange fRange;
108     private int fDocLen = -1;
109     
110     /** All external plug-in extension points */
111     private ArrayList JavaDoc fExternalExtPoints;
112
113     /** All internal plug-in extension points */
114     private ArrayList JavaDoc fInternalExtPoints;
115
116     /** All external and internal plug-in extension points */
117     private ArrayList JavaDoc fAllExtPoints;
118     
119     public XMLContentAssistProcessor(PDESourcePage sourcePage) {
120         fSourcePage = sourcePage;
121     }
122
123     public ICompletionProposal[] computeCompletionProposals(ITextViewer viewer, int offset) {
124         IDocument doc = viewer.getDocument();
125         int docLen = doc.getLength();
126         if (docLen == fDocLen)
127             return null; // left/right cursor has been pressed - cancel content assist
128

129         fDocLen = docLen;
130         IBaseModel model = getModel();
131         if (model instanceof AbstractEditingModel
132                 && fSourcePage.isDirty()
133                 && ((AbstractEditingModel)model).isStale()
134                 && fRange == null) {
135             ((AbstractEditingModel)model).reconciled(doc);
136         } else if (fAssistSessionStarted) {
137             // Always reconcile when content assist is first invoked
138
// Fix Bug # 149478
139
((AbstractEditingModel)model).reconciled(doc);
140             fAssistSessionStarted = false;
141         }
142
143         if (fRange == null) {
144             assignRange(offset);
145         } else {
146             // TODO - we may be looking at the wrong fRange
147
// when this happens --> reset it and reconcile
148
// how can we tell if we are looking at the wrong one... ?
149
boolean resetAndReconcile = false;
150             if (!(fRange instanceof IDocumentAttribute))
151                 // too easy to reconcile.. this is temporary
152
resetAndReconcile = true;
153                 
154             if (resetAndReconcile) {
155                 fRange = null;
156                 if (model instanceof IReconcilingParticipant)
157                     ((IReconcilingParticipant)model).reconciled(doc);
158             }
159         }
160         // Get content assist text if any
161
XMLContentAssistText caText = XMLContentAssistText.parse(offset, doc);
162
163         if (caText != null) {
164             return computeCATextProposal(doc, offset, caText);
165         } else if (fRange instanceof IDocumentAttribute) {
166             return computeCompletionProposal((IDocumentAttribute)fRange, offset, doc);
167         } else if (fRange instanceof IDocumentNode) {
168             return computeCompletionProposal((IDocumentNode)fRange, offset, doc);
169         } else if (fRange instanceof IDocumentTextNode) {
170             return null;
171         } else if (model instanceof PluginModelBase) {
172             // broken model - infer from text content
173
return computeBrokenModelProposal(((PluginModelBase)model).getLastErrorNode(), offset, doc);
174         }
175         return null;
176     }
177
178     /**
179      * @param proposals
180      * @param id
181      * @param print
182      * @return
183      */

184     protected ICompletionProposal[] debugPrintProposals(ICompletionProposal[] proposals, String JavaDoc id, boolean print) {
185         if (proposals == null) {
186             System.out.println("[0] " + id); //$NON-NLS-1$
187
return proposals;
188         }
189         System.out.println("[" + proposals.length + "] " + id); //$NON-NLS-1$ //$NON-NLS-2$
190
if (print == false) {
191             return proposals;
192         }
193         for (int i = 0; i < proposals.length; i++) {
194             System.out.println(proposals[i].getDisplayString());
195         }
196         return proposals;
197     }
198     
199     private void assignRange(int offset) {
200         fRange = fSourcePage.getRangeElement(offset, true);
201         if (fRange == null)
202             return;
203         // if we are rigth AT (cursor before) the range, we want to contribute
204
// to its parent
205
if (fRange instanceof IDocumentAttribute) {
206             if (((IDocumentAttribute)fRange).getNameOffset() == offset)
207                 fRange = ((IDocumentAttribute)fRange).getEnclosingElement();
208         } else if (fRange instanceof IDocumentNode) {
209             if (((IDocumentNode)fRange).getOffset() == offset)
210                 fRange = ((IDocumentNode)fRange).getParentNode();
211         } else if (fRange instanceof IDocumentTextNode) {
212             if (((IDocumentTextNode)fRange).getOffset() == offset)
213                 fRange = ((IDocumentTextNode)fRange).getEnclosingElement();
214         }
215     }
216
217     private ICompletionProposal[] computeCATextProposal(IDocument doc,
218             int offset, XMLContentAssistText caText) {
219         fRange = fSourcePage.getRangeElement(offset, true);
220         if ((fRange != null) &&
221                 (fRange instanceof IDocumentTextNode)) {
222             // We have a text node.
223
// Get its parent element
224
fRange = ((IDocumentTextNode)fRange).getEnclosingElement();
225         }
226         if ((fRange != null) &&
227                 (fRange instanceof IDocumentNode)) {
228             return computeAddChildProposal((IDocumentNode)fRange,
229                     caText.getStartOffset(), doc, caText.getText());
230         }
231         return null;
232     }
233     
234     private ICompletionProposal[] computeCompletionProposal(IDocumentAttribute attr, int offset, IDocument doc) {
235         if (offset < attr.getValueOffset())
236             return null;
237         int[] offests = new int[] {offset, offset, offset};
238         String JavaDoc[] guess = guessContentRequest(offests, doc, false);
239         if (guess == null)
240             return null;
241 // String element = guess[0];
242
// String attribute = guess[1];
243
String JavaDoc attrValue = guess[2];
244         
245         IPluginObject obj = XMLUtil.getTopLevelParent((IDocumentNode)attr);
246         if (obj instanceof IPluginExtension) {
247             if (attr.getAttributeName().equals(IPluginExtension.P_POINT) &&
248                     offset >= attr.getValueOffset()) {
249                 return computeExtPointAttrProposals(attr, offset, attrValue);
250             }
251             ISchemaAttribute sAttr = XMLUtil.getSchemaAttribute(attr, ((IPluginExtension)obj).getPoint());
252             if (sAttr == null)
253                 return null;
254             
255             if (sAttr.getKind() == IMetaAttribute.JAVA) {
256                 IResource resource = obj.getModel().getUnderlyingResource();
257                 if (resource == null)
258                     return null;
259                 // Revisit: NEW CODE HERE
260
ArrayList JavaDoc list = new ArrayList JavaDoc();
261                 ICompletionProposal[] proposals = null;
262                 
263                 generateTypePackageProposals(attrValue, resource.getProject(),
264                         list, offset - attrValue.length(), IJavaSearchConstants.CLASS_AND_INTERFACE);
265
266                 if ((list != null) && (list.size() != 0)) {
267                     // Convert the results array list into an array of completion
268
// proposals
269
proposals = (ICompletionProposal[]) list.toArray(new ICompletionProposal[list.size()]);
270                     sortCompletions(proposals);
271                     return proposals;
272                 }
273                 return null;
274             } else if (sAttr.getKind() == IMetaAttribute.RESOURCE) {
275                 // provide proposals with all resources in current plugin?
276

277             } else { // we have an IMetaAttribute.STRING kind
278
if (sAttr.getType() == null)
279                     return null;
280                 ISchemaRestriction sRestr = (sAttr.getType()).getRestriction();
281                 ArrayList JavaDoc objs = new ArrayList JavaDoc();
282                 if (sRestr == null) {
283                     ISchemaSimpleType type = sAttr.getType();
284                     if (type != null && type.getName().equals("boolean")) //$NON-NLS-1$
285
objs = F_V_BOOLS;
286                 } else {
287                     Object JavaDoc[] restrictions = sRestr.getChildren();
288                     for (int i = 0; i < restrictions.length; i++)
289                         if (restrictions[i] instanceof ISchemaObject)
290                             objs.add(new VirtualSchemaObject(((ISchemaObject)restrictions[i]).getName(), null, F_ATTRIBUTE_VALUE));
291                 }
292                 return computeAttributeProposal(attr, offset, attrValue, objs);
293             }
294         } else if (obj instanceof IPluginExtensionPoint) {
295             if (attr.getAttributeValue().equals(IPluginExtensionPoint.P_SCHEMA)) {
296                 // provide proposals with all schama files in current plugin?
297

298             }
299         }
300         return null;
301     }
302     
303     /**
304      * @param attribute
305      * @param offset
306      * @param currentAttributeValue
307      * @return
308      */

309     private ICompletionProposal[] computeExtPointAttrProposals(
310             IDocumentAttribute attribute, int offset, String JavaDoc currentAttributeValue) {
311         // Get all the applicable extension points
312
ArrayList JavaDoc allExtensionPoints =
313             getAllExtensionPoints(F_EXTENSION_ATTRIBUTE_POINT_VALUE);
314         // Ensure we found extension points
315
if ((allExtensionPoints == null) ||
316                 (allExtensionPoints.size() == 0)) {
317             return null;
318         }
319         // If there is no current attribute value, then the applicable extension
320
// points do not need to be filtered. Return the list as is
321
if ((currentAttributeValue == null) ||
322                 (currentAttributeValue.length() == 0)) {
323             return convertListToProposal(allExtensionPoints, (IDocumentRange)attribute, offset);
324         }
325         // Create the filtered proposal list
326
ArrayList JavaDoc filteredProposalList = new ArrayList JavaDoc();
327         // Filter the applicable extension points by the current attribute
328
// value
329
filterExtPointAttrProposals(filteredProposalList, allExtensionPoints, currentAttributeValue);
330         // Convert into a digestable list of proposals
331
return convertListToProposal(filteredProposalList, (IDocumentRange)attribute, offset);
332     }
333     
334     /**
335      * @param node
336      * @param offset
337      * @param filter
338      * @return
339      */

340     private ICompletionProposal[] computeRootNodeProposals(
341             IDocumentNode node, int offset, String JavaDoc filter) {
342         // Create the filtered proposal list
343
ArrayList JavaDoc filteredProposalList = new ArrayList JavaDoc();
344         // Add extension to the list
345
addToList(
346                 filteredProposalList,
347                 filter,
348                 new VirtualSchemaObject(F_STR_EXT,
349                         PDEUIMessages.XMLContentAssistProcessor_extensions,
350                         F_EXTENSION));
351         // Add extension point to the list
352
addToList(
353                 filteredProposalList,
354                 filter,
355                 new VirtualSchemaObject(F_STR_EXT_PT,
356                         PDEUIMessages.XMLContentAssistProcessor_extensionPoints,
357                         F_EXTENSION_POINT));
358         // Get all avaliable extensions with point attribute values
359
ArrayList JavaDoc allExtensionPoints =
360             getAllExtensionPoints(F_EXTENSION_POINT_AND_VALUE);
361         // Ensure we found extension points
362
if ((allExtensionPoints == null) ||
363                 (allExtensionPoints.size() == 0)) {
364             // Return the current proposal list without extension points
365
return convertListToProposal(filteredProposalList, node, offset);
366         }
367         // If there is no current value, then the applicable extension
368
// points do not need to be filtered. Merge the list as is with the
369
// existing proposals
370
if ((filter == null) ||
371                 (filter.length() == 0)) {
372             filteredProposalList.addAll(allExtensionPoints);
373             return convertListToProposal(filteredProposalList, node, offset);
374         }
375         // Filter the applicable extension points by the current value
376
filterExtPointAttrProposals(filteredProposalList, allExtensionPoints, filter);
377         // Convert into a digestable list of proposals
378
return convertListToProposal(filteredProposalList, node, offset);
379     }
380     
381     /**
382      * @param filteredProposalList - Must not be NULL
383      * @param allExtensionPoints - Must not be NULL
384      * @param filter - Must not be NULL
385      */

386     private void filterExtPointAttrProposals(
387             ArrayList JavaDoc filteredProposalList, ArrayList JavaDoc allExtensionPoints,
388             String JavaDoc filter) {
389         // Create a regex pattern out of the current attribute value
390
// Ignore case
391
String JavaDoc patternString = "(?i)" + filter; //$NON-NLS-1$
392
// Compile the pattern
393
Pattern JavaDoc pattern = Pattern.compile(patternString);
394         // Iterate over the applicable extension points and add extension points
395
// matching the pattern to the filtered proposal list
396
Iterator JavaDoc iterator = allExtensionPoints.iterator();
397         while (iterator.hasNext()) {
398             // Get the schema object
399
ISchemaObject schemaObject = (ISchemaObject)iterator.next();
400             // Ensure the schema object is defined
401
if (schemaObject == null) {
402                 continue;
403             }
404             // Get the name of the schema object
405
String JavaDoc name = schemaObject.getName();
406             // If the current attribute value matches some part of the name
407
// add it to the list
408
if (pattern.matcher(name).find()) {
409                 filteredProposalList.add(schemaObject);
410             }
411         }
412     }
413     
414     private ICompletionProposal[] computeAttributeProposal(IDocumentAttribute attr, int offset, String JavaDoc currValue, ArrayList JavaDoc validValues) {
415         if (validValues == null)
416             return null;
417         ArrayList JavaDoc list = new ArrayList JavaDoc();
418         for (int i = 0; i < validValues.size(); i++)
419             addToList(list, currValue, (ISchemaObject)validValues.get(i));
420         
421         return convertListToProposal(list, (IDocumentRange)attr, offset);
422     }
423
424     private ICompletionProposal[] computeCompletionProposal(IDocumentNode node, int offset, IDocument doc) {
425         int prop_type = determineAssistType(node, doc, offset);
426         switch (prop_type) {
427         case F_ADD_ATTRIB:
428             return computeAddAttributeProposal(F_INFER_BY_OBJECT, node, offset, doc, null, node.getXMLTagName());
429         case F_OPEN_TAG:
430             return computeOpenTagProposal(node, offset, doc);
431         case F_ADD_CHILD:
432             return computeAddChildProposal(node, offset, doc, null);
433         }
434         return null;
435     }
436     
437     private int determineAssistType(IDocumentNode node, IDocument doc, int offset) {
438         int len = node.getLength();
439         int off = node.getOffset();
440         if (len == -1 || off == -1)
441             return F_NO_ASSIST;
442         
443         offset = offset - off; // look locally
444
if (offset > node.getXMLTagName().length() + 1) {
445             try {
446                 String JavaDoc eleValue = doc.get(off, len);
447                 int ind = eleValue.indexOf('>');
448                 if (ind > 0 && eleValue.charAt(ind - 1) == '/')
449                     ind -= 1;
450                 if (offset <= ind) {
451                     if (canInsertAttrib(eleValue, offset))
452                         return F_ADD_ATTRIB;
453                     return F_NO_ASSIST;
454                 }
455                 ind = eleValue.lastIndexOf('<');
456                 if (ind == 0 && offset == len - 1)
457                     return F_OPEN_TAG; // childless node - check if it can be cracked open
458
if (ind + 1 < len && eleValue.charAt(ind + 1) == '/' && offset <= ind)
459                     return F_ADD_CHILD;
460             } catch (BadLocationException e) {
461             }
462         }
463         return F_NO_ASSIST;
464     }
465     
466     private boolean canInsertAttrib(String JavaDoc eleValue, int offset) {
467         // character before offset must be whitespace
468
// character on offset must be whitespace, '/' or '>'
469
char c = eleValue.charAt(offset);
470         return offset - 1 >= 0 && Character.isWhitespace(eleValue.charAt(offset - 1)) &&
471                     (Character.isWhitespace(c) || c == '/' || c == '>');
472     }
473     
474     private ICompletionProposal[] computeAddChildProposal(IDocumentNode node, int offset, IDocument doc, String JavaDoc filter) {
475         ArrayList JavaDoc propList = new ArrayList JavaDoc();
476         if (node instanceof IPluginBase) {
477             return computeRootNodeProposals(node, offset, filter);
478         } else if (node instanceof IPluginExtensionPoint) {
479             return null;
480         } else {
481             IPluginObject obj = XMLUtil.getTopLevelParent(node);
482             if (obj instanceof IPluginExtension) {
483                 ISchemaElement sElement = XMLUtil.getSchemaElement(node,
484                         ((IPluginExtension)obj).getPoint());
485                 if ((sElement != null) &&
486                         (sElement.getType() instanceof ISchemaComplexType)) {
487                     // We have a schema complex type. Either the element has attributes
488
// or the element has children.
489
// Generate the list of element proposals
490
HashSet JavaDoc elementSet = XMLElementProposalComputer
491                             .computeElementProposal(sElement, node);
492                     // Filter the list of element proposals
493
Iterator JavaDoc iterator = elementSet.iterator();
494                     while (iterator.hasNext()) {
495                         addToList(propList, filter, (ISchemaObject)iterator.next());
496                     }
497                 } else {
498                     return null;
499                 }
500             }
501         }
502         return convertListToProposal(propList, node, offset);
503     }
504     
505     private ICompletionProposal[] computeOpenTagProposal(IDocumentNode node, int offset, IDocument doc) {
506         IPluginObject obj = XMLUtil.getTopLevelParent(node);
507         if (obj instanceof IPluginExtension) {
508             ISchemaElement sElem = XMLUtil.getSchemaElement(node, ((IPluginExtension)obj).getPoint());
509             if (sElem == null)
510                 return null;
511             ISchemaCompositor comp = ((ISchemaComplexType)sElem.getType()).getCompositor();
512             if (comp != null)
513                 return new ICompletionProposal[] { new XMLCompletionProposal(node, null, offset, this) };
514         }
515         return null;
516     }
517
518     private ICompletionProposal[] computeAddAttributeProposal(int type, IDocumentNode node, int offset, IDocument doc, String JavaDoc filter, String JavaDoc tag) {
519         String JavaDoc nodeName = tag;
520         if (nodeName == null && node != null)
521             nodeName = node.getXMLTagName();
522         if (type == F_EXTENSION || node instanceof IPluginExtension) {
523             ISchemaElement sElem = XMLUtil.getSchemaElement(node, node != null ?
524                     ((IPluginExtension)node).getPoint() : null);
525             ISchemaObject[] sAttrs = sElem != null ?
526                     sElem.getAttributes() :
527                     new ISchemaObject[] {
528                         new VirtualSchemaObject(IIdentifiable.P_ID, PDEUIMessages.XMLContentAssistProcessor_extId, F_ATTRIBUTE),
529                         new VirtualSchemaObject(IPluginObject.P_NAME, PDEUIMessages.XMLContentAssistProcessor_extName, F_ATTRIBUTE),
530                         new VirtualSchemaObject(IPluginExtension.P_POINT, PDEUIMessages.XMLContentAssistProcessor_extPoint, F_ATTRIBUTE)
531                     };
532             return computeAttributeProposals(sAttrs, node, offset, filter, nodeName);
533         } else if (type == F_EXTENSION_POINT || node instanceof IPluginExtensionPoint) {
534             ISchemaObject[] sAttrs = new ISchemaObject[] {
535                         new VirtualSchemaObject(IIdentifiable.P_ID, PDEUIMessages.XMLContentAssistProcessor_extPointId, F_ATTRIBUTE),
536                         new VirtualSchemaObject(IPluginObject.P_NAME, PDEUIMessages.XMLContentAssistProcessor_extPointName, F_ATTRIBUTE),
537                         new VirtualSchemaObject(IPluginExtensionPoint.P_SCHEMA, PDEUIMessages.XMLContentAssistProcessor_schemaLocation, F_ATTRIBUTE)
538                     };
539             return computeAttributeProposals(sAttrs, node, offset, filter, nodeName);
540         } else {
541             IPluginObject obj = XMLUtil.getTopLevelParent(node);
542             if (obj instanceof IPluginExtension) {
543                 ISchemaElement sElem = XMLUtil.getSchemaElement(node, node != null ?
544                         ((IPluginExtension)obj).getPoint() : null);
545                 ISchemaObject[] sAttrs = sElem != null ? sElem.getAttributes() : null;
546                 return computeAttributeProposals(sAttrs, node, offset, filter, nodeName);
547             }
548         }
549         return null;
550     }
551
552     private void addToList(ArrayList JavaDoc list, String JavaDoc filter, ISchemaObject object) {
553         if (object == null)
554             return;
555         if (filter == null || filter.length() == 0)
556             list.add(object);
557         else {
558             String JavaDoc name = object.getName();
559             if (filter.regionMatches(true, 0, name, 0, filter.length()))
560                 list.add(object);
561         }
562     }
563     
564     private ICompletionProposal[] computeBrokenModelProposal(IDocumentNode parent, int offset, IDocument doc) {
565         if (parent == null)
566             return null;
567         
568         int[] offArr = new int[] {offset, offset, offset};
569         String JavaDoc[] guess = guessContentRequest(offArr, doc, true);
570         if (guess == null)
571             return null;
572         
573         int elRepOffset = offArr[0];
574         int atRepOffset = offArr[1];
575         int atValRepOffest = offArr[2];
576         String JavaDoc element = guess[0];
577         String JavaDoc attr = guess[1];
578         String JavaDoc attVal = guess[2];
579         
580         IPluginObject obj = XMLUtil.getTopLevelParent(parent);
581         if (obj instanceof IPluginExtension) {
582             String JavaDoc point = ((IPluginExtension)obj).getPoint();
583             if (attr == null)
584                 // search for element proposals
585
return computeAddChildProposal(parent, elRepOffset, doc, element);
586             
587             ISchemaElement sEle = XMLUtil.getSchemaElement(parent, point);
588             if (sEle == null)
589                 return null;
590             sEle = sEle.getSchema().findElement(element);
591             if (sEle == null)
592                 return null;
593             
594             if (attr.indexOf('=') != -1)
595                 // search for attribute content proposals
596
return computeBrokenModelAttributeContentProposal(parent, atValRepOffest, element, attr, attVal);
597             
598             // search for attribute proposals
599
return computeAttributeProposals(sEle.getAttributes(), null, atRepOffset, attr, element);
600         } else if (parent instanceof IPluginBase) {
601             if (attr == null)
602                 return computeAddChildProposal(parent, elRepOffset, doc, element);
603             if (element.equalsIgnoreCase(F_STR_EXT))
604                 return computeAddAttributeProposal(F_EXTENSION, null, atRepOffset, doc, attr, F_STR_EXT);
605             if (element.equalsIgnoreCase(F_STR_EXT_PT))
606                 return computeAddAttributeProposal(F_EXTENSION_POINT, null, atRepOffset, doc, attr, F_STR_EXT_PT);
607         }
608         return null;
609     }
610     
611     private ICompletionProposal[] computeBrokenModelAttributeContentProposal(IDocumentNode parent, int offset, String JavaDoc element, String JavaDoc attr, String JavaDoc filter) {
612         // TODO use computeCompletionProposal(IDocumentAttribute attr, int offset) if possible
613
// or refactor above to be used here
614
// CURRENTLY: attribute completion only works in non-broken models
615
return null;
616     }
617     
618     private String JavaDoc[] guessContentRequest(int[] offset, IDocument doc, boolean brokenModel) {
619         StringBuffer JavaDoc nodeBuffer = new StringBuffer JavaDoc();
620         StringBuffer JavaDoc attrBuffer = new StringBuffer JavaDoc();
621         StringBuffer JavaDoc attrValBuffer = new StringBuffer JavaDoc();
622         String JavaDoc node = null;
623         String JavaDoc attr = null;
624         String JavaDoc attVal = null;
625         int quoteCount = 0;
626         try {
627             while (--offset[0] >= 0) {
628                 char c = doc.getChar(offset[0]);
629                 if (c == '"') {
630                     quoteCount += 1;
631                     nodeBuffer.setLength(0);
632                     attrBuffer.setLength(0);
633                     if (attVal != null) // ran into 2nd quotation mark, we are out of range
634
continue;
635                     offset[2] = offset[0];
636                     attVal = attrValBuffer.toString();
637                 } else if (Character.isWhitespace(c)) {
638                     nodeBuffer.setLength(0);
639                     if (attr == null) {
640                         offset[1] = offset[0];
641                         int attBuffLen = attrBuffer.length();
642                         if (attBuffLen > 0 && attrBuffer.charAt(attBuffLen - 1) == '=')
643                             attrBuffer.setLength(attBuffLen - 1);
644                         attr = attrBuffer.toString();
645                     }
646                 } else if (c == '<') {
647                     node = nodeBuffer.toString();
648                     break;
649                 } else if (c == '>') {
650                     // only enable content assist if user is inside an open tag
651
return null;
652                 } else {
653                     attrValBuffer.insert(0, c);
654                     attrBuffer.insert(0, c);
655                     nodeBuffer.insert(0, c);
656                 }
657             }
658         } catch (BadLocationException e) {}
659         if (node == null)
660             return null;
661         
662         if (quoteCount % 2 == 0)
663             attVal = null;
664         else if (brokenModel)
665             return null; // open quotes - don't provide assist
666

667         return new String JavaDoc[] {node, attr, attVal};
668     }
669     
670     protected IBaseModel getModel()
671         { return fSourcePage.getInputContext().getModel(); }
672     protected ITextSelection getCurrentSelection() {
673         ISelection sel = fSourcePage.getSelectionProvider().getSelection();
674         if (sel instanceof ITextSelection)
675             return (ITextSelection)sel;
676         return null;
677     }
678     protected void flushDocument() {
679         fSourcePage.getInputContext().flushEditorInput();
680     }
681     
682     private ICompletionProposal[] computeAttributeProposals(ISchemaObject[] sAttrs, IDocumentNode node, int offset, String JavaDoc filter, String JavaDoc parentName) {
683         if (sAttrs == null || sAttrs.length == 0)
684             return null;
685         IDocumentAttribute[] attrs = node != null ? node.getNodeAttributes() : new IDocumentAttribute[0];
686         
687         ArrayList JavaDoc list = new ArrayList JavaDoc();
688         for (int i = 0; i < sAttrs.length; i++) {
689             int k; // if we break early we wont add
690
for (k = 0; k < attrs.length; k++)
691                 if (attrs[k].getAttributeName().equals(sAttrs[i].getName()))
692                     break;
693             if (k == attrs.length)
694                 addToList(list, filter, sAttrs[i]);
695         }
696         if (filter != null && filter.length() == 0)
697             list.add(0, new VirtualSchemaObject(parentName, null, F_CLOSE_TAG));
698         return convertListToProposal(list, node, offset);
699     }
700
701     private ICompletionProposal[] convertListToProposal(ArrayList JavaDoc list, IDocumentRange range, int offset) {
702         ICompletionProposal[] proposals = new ICompletionProposal[list.size()];
703         if (proposals.length == 0)
704             return null;
705         for (int i = 0; i < proposals.length; i++)
706             proposals[i] = new XMLCompletionProposal(range, (ISchemaObject)list.get(i), offset, this);
707         return proposals;
708     }
709
710     public void assistSessionEnded(ContentAssistEvent event) {
711         fRange = null;
712         
713         // Reset cached internal and external extension point proposals
714
fAllExtPoints = null;
715         // Reset cached internal point proposals
716
// Note: Not resetting cached external point proposals
717
// Assumption is that the users workspace can change; but, not the
718
// platform including all the external plugins
719
fInternalExtPoints = null;
720         
721         fDocLen = -1;
722     }
723
724     public void assistSessionStarted(ContentAssistEvent event) {
725         fAssistSessionStarted = true;
726     }
727
728     public void selectionChanged(ICompletionProposal proposal, boolean smartToggle) {
729     }
730
731     /**
732      * @param vSchemaType
733      * @return
734      */

735     private ArrayList JavaDoc getAllExtensionPoints(int vSchemaType) {
736         // Return the previous extension points if defined
737
if (fAllExtPoints != null) {
738             return fAllExtPoints;
739         }
740         // Get the plugin model base
741
IPluginModelBase model = getPluginModelBase();
742         // Note: All plug-in extension points are cached except the
743
// extension points defined by the plugin.xml we are currently
744
// editing. This means if a plug-in in the workspace defines a new
745
// extension point and the plugin.xml editor is still open, the
746
// new extension point will not show up as a proposal because it is
747
// using a cached list of extension points.
748
// External extensions points are all extension points not defined by
749
// the plugin currently being edited - opposed to non-workpspace
750
// plugins. Internal extension points are the extension points defined
751
// by the plugin currently being edited. May want to modify this
752
// behaviour in the future.
753
// Add all external extension point proposals to the list
754
fAllExtPoints =
755             (ArrayList JavaDoc)getExternalExtensionPoints(model, vSchemaType).clone();
756         // Add all internal extension point proposals to the list
757
fAllExtPoints.addAll(getInternalExtensionPoints(model, vSchemaType));
758         
759         return fAllExtPoints;
760     }
761     
762     /**
763      * @param model
764      * @return
765      */

766     private ArrayList JavaDoc getExternalExtensionPoints(IPluginModelBase model,
767             int vSchemaType) {
768         // Return the previous external extension points if defined
769
if (fExternalExtPoints != null) {
770             updateExternalExtPointTypes(vSchemaType);
771             return fExternalExtPoints;
772         }
773         // Query for all external extension points
774
fExternalExtPoints = new ArrayList JavaDoc();
775         // Get all plug-ins in the workspace
776
IPluginModelBase[] plugins = PluginRegistry.getActiveModels();
777         // Process each plugin
778
for (int i = 0; i < plugins.length; i++) {
779             // Make sure this plugin is not the one we are currently
780
// editing which defines internal extension points.
781
// We don't want to cache internal extension points because the
782
// workspace can change.
783
if (plugins[i].getPluginBase().getId().equals(
784                     model.getPluginBase().getId())) {
785                 // Skip this plugin
786
continue;
787             }
788             // Get all extension points defined by this plugin
789
IPluginExtensionPoint[] points =
790                 plugins[i].getPluginBase().getExtensionPoints();
791             // Process each extension point
792
for (int j = 0; j < points.length; j++) {
793                 VirtualSchemaObject vObject = new VirtualSchemaObject(
794                         IdUtil.getFullId(points[j], model),
795                         points[j],
796                         vSchemaType);
797                 // Add the proposal to the list
798
fExternalExtPoints.add(vObject);
799             }
800         }
801         return fExternalExtPoints;
802     }
803     
804     /**
805      * Handles edge case.
806      * External extension points are cached.
807      * As a result, these types persist over each other depending on what
808      * context content assist is invoked first:
809      * F_EXTENSION_ATTRIBUTE_POINT_VALUE
810      * F_EXTENSION_POINT_AND_VALUE
811      * @param newVType
812      */

813     private void updateExternalExtPointTypes(int newVType) {
814         // Ensure we have proposals
815
if (fExternalExtPoints.size() == 0) {
816             return;
817         }
818         // Get the first proposal
819
VirtualSchemaObject vObject =
820             (VirtualSchemaObject)fExternalExtPoints.get(0);
821         // If the first proposals type is the same as the new type, then we
822
// do not have to do the update. That is because all the proposals
823
// have the same type.
824
if (vObject.getVType() == newVType) {
825             return;
826         }
827         // Update all the proposals with the new type
828
Iterator JavaDoc iterator = fExternalExtPoints.iterator();
829         while (iterator.hasNext()) {
830             ((VirtualSchemaObject)iterator.next()).setVType(newVType);
831         }
832     }
833     
834     /**
835      * @param model
836      * @return
837      */

838     private ArrayList JavaDoc getInternalExtensionPoints(IPluginModelBase model,
839             int vSchemaType) {
840         // Return the previous internal extension points if defined
841
if (fInternalExtPoints != null) {
842             // Realistically, this line should never be hit
843
return fInternalExtPoints;
844         }
845         fInternalExtPoints = new ArrayList JavaDoc();
846         // Get all extension points defined by this plugin
847
IPluginExtensionPoint[] points =
848             model.getPluginBase().getExtensionPoints();
849         // Process each extension point
850
for (int j = 0; j < points.length; j++) {
851             VirtualSchemaObject vObject = new VirtualSchemaObject(
852                     IdUtil.getFullId(points[j], model),
853                     points[j],
854                     vSchemaType);
855             // Add the proposal to the list
856
fInternalExtPoints.add(vObject);
857         }
858         return fInternalExtPoints;
859     }
860     
861     /**
862      * Returns a BundlePluginModel which has a getId() method that works.
863      * getModel() method returns a PluginModel whose getId() method does not
864      * work.
865      * @return
866      */

867     private IPluginModelBase getPluginModelBase() {
868         FormEditor formEditor = fSourcePage.getEditor();
869         if ((formEditor instanceof PDEFormEditor) == false) {
870             return null;
871         }
872         IBaseModel bModel = ((PDEFormEditor)formEditor).getAggregateModel();
873         if ((bModel instanceof IPluginModelBase) == false) {
874             return null;
875         }
876         return (IPluginModelBase)bModel;
877     }
878     
879     public Image getImage(int type) {
880         if (fImages[type] == null) {
881             switch(type) {
882             case F_EXTENSION_POINT:
883             case F_EXTENSION_ATTRIBUTE_POINT_VALUE:
884                 return fImages[type] = PDEPluginImages.DESC_EXT_POINT_OBJ.createImage();
885             case F_EXTENSION_POINT_AND_VALUE:
886             case F_EXTENSION:
887                 return fImages[type] = PDEPluginImages.DESC_EXTENSION_OBJ.createImage();
888             case F_ELEMENT:
889             case F_CLOSE_TAG:
890                 return fImages[type] = PDEPluginImages.DESC_XML_ELEMENT_OBJ.createImage();
891             case F_ATTRIBUTE:
892             case F_ATTRIBUTE_VALUE:
893                 return fImages[type] = PDEPluginImages.DESC_ATT_URI_OBJ.createImage();
894             }
895         }
896         return fImages[type];
897     }
898     
899     public void dispose() {
900         for (int i = 0; i < fImages.length; i++)
901             if (fImages[i] != null && !fImages[i].isDisposed())
902                 fImages[i].dispose();
903     }
904     
905     /**
906      * @return
907      */

908     public PDESourcePage getSourcePage() {
909         return fSourcePage;
910     }
911     
912 }
913
Popular Tags