com.siteview.mde.internal.ui.editor.contentassist.XMLCompletionProposal.java Source code

Java tutorial

Introduction

Here is the source code for com.siteview.mde.internal.ui.editor.contentassist.XMLCompletionProposal.java

Source

/*******************************************************************************
 * Copyright (c) 2006, 2010 IBM Corporation and others.
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/epl-v10.html
 *
 * Contributors:
 *     IBM Corporation - initial API and implementation
 *     Benjamin Cabe <benjamin.cabe@anyware-tech.com> - bug 227105
 *******************************************************************************/

package com.siteview.mde.internal.ui.editor.contentassist;

import com.siteview.mde.internal.core.text.monitor.MonitorAttribute;

import com.siteview.mde.core.monitor.*;

import java.util.HashSet;
import java.util.Stack;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.jface.internal.text.html.BrowserInformationControl;
import org.eclipse.jface.internal.text.html.HTMLPrinter;
import org.eclipse.jface.resource.JFaceResources;
import org.eclipse.jface.text.*;
import org.eclipse.jface.text.contentassist.*;
import com.siteview.mde.core.IBaseModel;
import com.siteview.mde.internal.core.ischema.*;
import com.siteview.mde.internal.core.text.*;
import com.siteview.mde.internal.ui.MDEPlugin;
import com.siteview.mde.internal.ui.MDEUIMessages;
import com.siteview.mde.internal.ui.editor.MDESourcePage;
import com.siteview.mde.internal.ui.editor.text.XMLUtil;
import com.siteview.mde.internal.ui.util.TextUtil;
import org.eclipse.swt.graphics.Image;
import org.eclipse.swt.graphics.Point;
import org.eclipse.swt.widgets.Shell;

public class XMLCompletionProposal implements ICompletionProposal, ICompletionProposalExtension3,
        ICompletionProposalExtension4, ICompletionProposalExtension5 {

    private static final String F_DEF_ATTR_INDENT = "      "; //$NON-NLS-1$

    private ISchemaObject fSchemaObject;
    private IDocumentRange fRange;
    private int fOffset;
    private int fLen;
    private int fSelOffset;
    private int fSelLen;
    private XMLContentAssistProcessor fProcessor;
    private String fAddInfo;
    private IInformationControlCreator fCreator;

    private IMonitorParent fPluginParent;
    private ISchemaElement fSchemaElement;

    public XMLCompletionProposal(IDocumentRange node, ISchemaObject object, int offset,
            XMLContentAssistProcessor processor) {
        fLen = -1;
        fSelOffset = -1;
        fSelLen = 0;
        fRange = node;
        fSchemaObject = object;
        fOffset = offset;
        fProcessor = processor;
    }

    public void apply(IDocument document) {
        ITextSelection sel = fProcessor.getCurrentSelection();
        if (sel == null) {
            return;
        }
        fLen = sel.getLength() + sel.getOffset() - fOffset;
        if (fLen < 0) {
            // If the cursor is moved after the popup is opened sometimes fLen can be negative, see bug 266083
            fLen = 0;
        }
        String delim = TextUtilities.getDefaultLineDelimiter(document);
        StringBuffer documentInsertBuffer = new StringBuffer();
        boolean doInternalWork = false;
        // Generate the text to apply depending on the proposal type
        if (fSchemaObject instanceof ISchemaAttribute) {
            applyAttribute(documentInsertBuffer);
        } else if (fSchemaObject instanceof ISchemaElement) {
            applyElement(getIndent(document, fOffset), delim, documentInsertBuffer);
            doInternalWork = true;
        } else if (fSchemaObject instanceof VirtualSchemaObject) {
            doInternalWork = applyVirtual(document, sel, delim, documentInsertBuffer, doInternalWork);
        }
        // Check if there is anything to apply
        if (documentInsertBuffer.length() == 0) {
            return;
        }
        // Apply the proposal to the document
        try {
            document.replace(fOffset, fLen, documentInsertBuffer.toString());
        } catch (BadLocationException e) {
            MDEPlugin.log(e);
        }
        // Update the model if necessary
        if (doInternalWork) {
            modifyModel(document);
        }
    }

    /**
     * @param document
     * @param sel
     * @param delim
     * @param documentInsertBuffer
     * @param doInternalWork
     */
    private boolean applyVirtual(IDocument document, ITextSelection sel, String delim,
            StringBuffer documentInsertBuffer, boolean doInternalWork) {
        int type = ((VirtualSchemaObject) fSchemaObject).getVType();
        switch (type) {
        case XMLContentAssistProcessor.F_ATTRIBUTE:
            applyAttribute(documentInsertBuffer);
            break;
        case XMLContentAssistProcessor.F_CLOSE_TAG:
            fOffset = sel.getOffset();
            fLen = 0;
            documentInsertBuffer.append(" />"); //$NON-NLS-1$
            break;
        case XMLContentAssistProcessor.F_EXTENSION:
            applyExtension(document, delim, documentInsertBuffer);
            break;
        case XMLContentAssistProcessor.F_EXTENSION_POINT:
            applyExtensionPoint(documentInsertBuffer);
            break;
        case XMLContentAssistProcessor.F_EXTENSION_POINT_AND_VALUE:
            doInternalWork = true; // we will want to add required child nodes/attributes
            applyExtensionFullPoint(document, delim, documentInsertBuffer);
            break;
        case XMLContentAssistProcessor.F_EXTENSION_ATTRIBUTE_POINT_VALUE:
            doInternalWork = true; // we will want to add required child nodes/attributes
        case XMLContentAssistProcessor.F_ATTRIBUTE_VALUE:
        case XMLContentAssistProcessor.F_ATTRIBUTE_BOOLEAN_VALUE:
        case XMLContentAssistProcessor.F_ATTRIBUTE_ID_VALUE:
            applyAttributeValue(document, documentInsertBuffer);
            break;
        }
        return doInternalWork;
    }

    /**
     * @param document
     * @param documentInsertBuffer
     */
    private void applyAttributeValue(IDocument document, StringBuffer documentInsertBuffer) {
        if (fRange instanceof IDocumentAttributeNode) {
            fOffset = ((IDocumentAttributeNode) fRange).getValueOffset();
            String value = fSchemaObject.getName();
            try {
                // add indentation
                int off = fOffset;
                int docLen = document.getLength();
                fLen = 0;
                while (off < docLen) {
                    char c = document.getChar(off++);
                    if (c == '"')
                        break;
                    fLen += 1;
                }
            } catch (BadLocationException e) {
            }
            documentInsertBuffer.append(value);
            fSelOffset = fOffset + value.length();
        }
    }

    /**
     * @param documentInsertBuffer
     */
    private void applyExtensionPoint(StringBuffer documentInsertBuffer) {
        String id = "id"; //$NON-NLS-1$
        documentInsertBuffer.append("<extension-point id=\""); //$NON-NLS-1$
        fSelOffset = fOffset + documentInsertBuffer.length();
        fSelLen = id.length();
        documentInsertBuffer.append(id);
        documentInsertBuffer.append("\" name=\"name\" />"); //$NON-NLS-1$
    }

    /**
     * @param document
     * @param delim
     * @param documentInsertBuffer
     */
    private void applyExtension(IDocument document, String delim, StringBuffer documentInsertBuffer) {
        documentInsertBuffer.append("<extension"); //$NON-NLS-1$
        documentInsertBuffer.append(delim);
        String indent = getIndent(document, fOffset);
        documentInsertBuffer.append(indent);
        documentInsertBuffer.append(F_DEF_ATTR_INDENT);
        documentInsertBuffer.append("point=\"\">"); //$NON-NLS-1$
        fSelOffset = fOffset + documentInsertBuffer.length() - 2; // position rigth inside new point="" attribute
        documentInsertBuffer.append(delim);
        documentInsertBuffer.append(indent);
        documentInsertBuffer.append("</extension>"); //$NON-NLS-1$
    }

    /**
     * @param document
     * @param delim
     * @param documentInsertBuffer
     */
    private void applyExtensionFullPoint(IDocument document, String delim, StringBuffer documentInsertBuffer) {

        String pointID = fSchemaObject.getName();
        String indent = getIndent(document, fOffset);
        // Add extension mark-up to the buffer right up until the point 
        // attribute value
        documentInsertBuffer.append('<');
        documentInsertBuffer.append("extension"); //$NON-NLS-1$
        documentInsertBuffer.append(delim);
        documentInsertBuffer.append(indent);
        documentInsertBuffer.append(F_DEF_ATTR_INDENT);
        documentInsertBuffer.append("point"); //$NON-NLS-1$
        documentInsertBuffer.append('=');
        documentInsertBuffer.append('"');
        // Calculate the offset for the start of the selection
        // We want to select the point attribute value in between the quotes
        // fOffset is the point where content assist was first invoked
        fSelOffset = fOffset + documentInsertBuffer.length();
        // Calculate the selection length
        fSelLen = pointID.length();
        // Add extension mark-up to the buffer including the point attribute
        // value and beyond
        documentInsertBuffer.append(pointID);
        documentInsertBuffer.append('"');
        documentInsertBuffer.append('>');
        documentInsertBuffer.append(delim);
        documentInsertBuffer.append(indent);
        documentInsertBuffer.append('<');
        documentInsertBuffer.append('/');
        documentInsertBuffer.append("extension"); //$NON-NLS-1$
        documentInsertBuffer.append('>');
    }

    /**
     * @param document
     * @param delim
     * @param sb
     */
    private void applyElement(String indent, String delim, StringBuffer documentInsertBuffer) {
        documentInsertBuffer.append('<');
        documentInsertBuffer.append(((ISchemaElement) fSchemaObject).getName());
        documentInsertBuffer.append('>');
        documentInsertBuffer.append(delim);
        documentInsertBuffer.append(indent);
        documentInsertBuffer.append('<');
        documentInsertBuffer.append('/');
        documentInsertBuffer.append(((ISchemaElement) fSchemaObject).getName());
        documentInsertBuffer.append('>');
    }

    /**
     * @param sb
     */
    private void applyAttribute(StringBuffer documentInsertBuffer) {
        if (fRange == null) {
            // Model is broken
            // Manually adjust offsets
            fLen -= 1;
            fOffset += 1;
        }
        String attName = fSchemaObject.getName();
        documentInsertBuffer.append(attName);
        documentInsertBuffer.append("=\""); //$NON-NLS-1$
        fSelOffset = fOffset + documentInsertBuffer.length();
        String value = attName;
        if (fSchemaObject instanceof ISchemaAttribute) {
            value = XMLInsertionComputer.generateAttributeValue((ISchemaAttribute) fSchemaObject,
                    fProcessor.getModel(), attName);
        }
        documentInsertBuffer.append(value);
        fSelLen = value.length();
        documentInsertBuffer.append('"');
    }

    private void modifyModel(IDocument document) {
        // TODO requires
        //  - refactoring
        //  - better grouping of cases (if statements)
        IBaseModel model = fProcessor.getModel();
        if (model instanceof IReconcilingParticipant)
            ((IReconcilingParticipant) model).reconciled(document);

        if (model instanceof IMonitorModelBase) {
            IMonitorBase base = ((IMonitorModelBase) model).getMonitorBase();

            fPluginParent = null;
            fSchemaElement = null;

            if (fSchemaObject instanceof VirtualSchemaObject) {
                switch (((VirtualSchemaObject) fSchemaObject).getVType()) {
                case XMLContentAssistProcessor.F_EXTENSION_ATTRIBUTE_POINT_VALUE:
                    if (!(fRange instanceof IDocumentAttributeNode))
                        break;
                    int offset = ((IDocumentAttributeNode) fRange).getEnclosingElement().getOffset();
                    IMonitorExtension[] extensions = base.getExtensions();
                    for (int i = 0; i < extensions.length; i++) {
                        if (((IDocumentElementNode) extensions[i]).getOffset() == offset) {
                            if (extensions[i].getChildCount() != 0)
                                break; // don't modify existing extensions
                            fPluginParent = extensions[i];
                            fSchemaElement = XMLUtil.getSchemaElement((IDocumentElementNode) extensions[i],
                                    extensions[i].getPoint());
                            break;
                        }
                    }
                    break;
                case XMLContentAssistProcessor.F_EXTENSION_POINT_AND_VALUE:
                    findExtensionVirtualPointValue(base);
                    break;
                }
            } else if (fRange instanceof IDocumentElementNode && base instanceof IDocumentElementNode) {
                Stack s = new Stack();
                IDocumentElementNode node = (IDocumentElementNode) fRange;
                IDocumentElementNode newSearch = (IDocumentElementNode) base;
                // traverse up old model, pushing all nodes onto the stack along the way
                while (node != null && !(node instanceof IMonitorBase)) {
                    s.push(node);
                    node = node.getParentNode();
                }

                // traverse down new model to find new node, using stack as a guideline
                while (!s.isEmpty()) {
                    node = (IDocumentElementNode) s.pop();
                    int nodeIndex = 0;
                    while ((node = node.getPreviousSibling()) != null)
                        nodeIndex += 1;
                    newSearch = newSearch.getChildAt(nodeIndex);
                }
                if (newSearch != null) {
                    IDocumentElementNode[] children = newSearch.getChildNodes();
                    for (int i = 0; i < children.length; i++) {
                        if (children[i].getOffset() == fOffset && children[i] instanceof IMonitorElement) {
                            fPluginParent = (IMonitorElement) children[i];
                            fSchemaElement = (ISchemaElement) fSchemaObject;
                            break;
                        }
                    }
                }
            }

            if (fPluginParent != null && fSchemaElement != null) {
                XMLInsertionComputer.computeInsertion(fSchemaElement, fPluginParent);
                fProcessor.flushDocument();
                if (model instanceof AbstractEditingModel) {
                    try {
                        ((AbstractEditingModel) model).adjustOffsets(document);
                    } catch (CoreException e) {
                    }
                    setSelectionOffsets(document, fSchemaElement, fPluginParent);
                }
            }
        }
    }

    /**
     * Assumption: Model already reconciled by caller
     * @param base
     */
    private void findExtensionVirtualPointValue(IMonitorBase base) {

        IDocumentRange range = null;
        MDESourcePage page = fProcessor.getSourcePage();
        // Ensure page is defined
        if (page == null) {
            return;
        }
        // When we inserted the extension element and extension point attribute
        // name and value, we selected the point value
        // Find the corresponding range in order to add child elements to
        // the proper extension.
        range = page.getRangeElement(fOffset, true);
        // Ensure the range is an attribute
        if ((range == null) || (range instanceof IDocumentElementNode) == false) {
            return;
        }
        // Get the offset of the extension element
        int targetOffset = ((IDocumentElementNode) range).getOffset();
        // Search this plug-ins extensions for the proper one
        IMonitorExtension[] extensions = base.getExtensions();
        for (int i = 0; i < extensions.length; i++) {
            // Get the offset of the current extension
            int extensionOffset = ((IDocumentElementNode) extensions[i]).getOffset();
            // If the offsets match we foudn the extension element
            // Note: The extension element should have no children
            if ((extensionOffset == targetOffset) && (extensions[i].getChildCount() == 0)) {
                fPluginParent = extensions[i];
                // Get the corresponding schema element
                fSchemaElement = XMLUtil.getSchemaElement((IDocumentElementNode) extensions[i],
                        extensions[i].getPoint());
                break;
            }
        }
    }

    private void setSelectionOffsets(IDocument document, ISchemaElement schemaElement,
            IMonitorParent pluginParent) {
        if (pluginParent instanceof IMonitorExtension) {
            String point = ((IMonitorExtension) pluginParent).getPoint();
            IMonitorObject[] children = ((IMonitorExtension) pluginParent).getChildren();
            if (children != null && children.length > 0 && children[0] instanceof IMonitorParent) {
                pluginParent = (IMonitorParent) children[0];
                schemaElement = XMLUtil.getSchemaElement((IDocumentElementNode) pluginParent, point);
            }
        }

        if (pluginParent instanceof IMonitorElement) {
            int offset = ((IDocumentElementNode) pluginParent).getOffset();
            int len = ((IDocumentElementNode) pluginParent).getLength();
            String value = null;
            try {
                value = document.get(offset, len);
            } catch (BadLocationException e) {
            }
            if (((IMonitorElement) pluginParent).getAttributeCount() > 0) {
                // Select value of first required attribute
                IMonitorAttribute att = ((IMonitorElement) pluginParent).getAttributes()[0];
                if (att instanceof MonitorAttribute) {
                    fSelOffset = ((MonitorAttribute) att).getValueOffset();
                    fSelLen = ((MonitorAttribute) att).getValueLength();
                }
            } else if (XMLInsertionComputer.hasOptionalChildren(schemaElement, false, new HashSet())
                    && value != null) {
                int ind = value.indexOf('>');
                if (ind > 0) {
                    fSelOffset = offset + ind + 1;
                    fSelLen = 0;
                }
            } else if (XMLInsertionComputer.hasOptionalAttributes(schemaElement) && value != null) {
                int ind = value.indexOf('>');
                if (ind != -1) {
                    fSelOffset = offset + ind;
                    fSelLen = 0;
                }
            } else {
                // position caret after element
                fSelOffset = offset + len;
                fSelLen = 0;
            }
        }
    }

    private String getIndent(IDocument document, int offset) {
        StringBuffer indBuff = new StringBuffer();
        try {
            // add indentation
            int line = document.getLineOfOffset(offset);
            int lineOffset = document.getLineOffset(line);
            int indent = offset - lineOffset;
            char[] indentChars = document.get(lineOffset, indent).toCharArray();
            // for every tab append a tab, for anything else append a space
            for (int i = 0; i < indentChars.length; i++)
                indBuff.append(indentChars[i] == '\t' ? '\t' : ' ');
        } catch (BadLocationException e) {
        }
        return indBuff.toString();
    }

    public String getAdditionalProposalInfo() {
        if (fAddInfo == null) {
            if (fSchemaObject == null)
                return null;
            StringBuffer sb = new StringBuffer();
            HTMLPrinter.insertPageProlog(sb, 0, TextUtil.getJavaDocStyleSheerURL());
            String desc = null;
            if (fSchemaObject == null)
                desc = MDEUIMessages.BaseWizardSelectionPage_noDesc;
            else {
                desc = fSchemaObject.getDescription();
                if (desc == null || desc.trim().length() == 0)
                    desc = MDEUIMessages.BaseWizardSelectionPage_noDesc;
            }
            sb.append(desc);
            HTMLPrinter.addPageEpilog(sb);
            fAddInfo = sb.toString();
        }
        return fAddInfo;
    }

    public IContextInformation getContextInformation() {
        return null;
    }

    public String getDisplayString() {
        if (fSchemaObject instanceof VirtualSchemaObject) {
            switch (((VirtualSchemaObject) fSchemaObject).getVType()) {
            case XMLContentAssistProcessor.F_CLOSE_TAG:
                return "... />"; //$NON-NLS-1$
            case XMLContentAssistProcessor.F_EXTENSION_POINT_AND_VALUE:
            case XMLContentAssistProcessor.F_EXTENSION_ATTRIBUTE_POINT_VALUE:
            case XMLContentAssistProcessor.F_ATTRIBUTE_VALUE:
                return fSchemaObject.getName();
            }
        }
        if (fSchemaObject instanceof ISchemaAttribute)
            return fSchemaObject.getName();
        if (fSchemaObject != null)
            return fSchemaObject.getName();
        if (fRange instanceof IDocumentElementNode)
            return "...> </" + ((IDocumentElementNode) fRange).getXMLTagName() + ">"; //$NON-NLS-1$ //$NON-NLS-2$
        return null;
    }

    public Image getImage() {
        if (fSchemaObject instanceof VirtualSchemaObject)
            return fProcessor.getImage(((VirtualSchemaObject) fSchemaObject).getVType());
        if (fSchemaObject instanceof ISchemaAttribute)
            return fProcessor.getImage(XMLContentAssistProcessor.F_ATTRIBUTE);
        if (fSchemaObject instanceof ISchemaElement || fSchemaObject == null)
            return fProcessor.getImage(XMLContentAssistProcessor.F_ELEMENT);
        return null;
    }

    public Point getSelection(IDocument document) {
        if (fSelOffset == -1)
            return null;
        return new Point(fSelOffset, fSelLen);
    }

    public Object getAdditionalProposalInfo(IProgressMonitor monitor) {
        return getAdditionalProposalInfo();
    }

    public IInformationControlCreator getInformationControlCreator() {
        if (fCreator == null) {
            fCreator = new AbstractReusableInformationControlCreator() {
                public IInformationControl doCreateInformationControl(Shell parent) {
                    if (BrowserInformationControl.isAvailable(parent))
                        return new BrowserInformationControl(parent, JFaceResources.DIALOG_FONT, false);
                    return new DefaultInformationControl(parent, false);
                }
            };
        }
        return fCreator;
    }

    public int getPrefixCompletionStart(IDocument document, int completionOffset) {
        return 0;
    }

    public CharSequence getPrefixCompletionText(IDocument document, int completionOffset) {
        return null;
    }

    /* (non-Javadoc)
     * @see org.eclipse.jface.text.contentassist.ICompletionProposalExtension4#isAutoInsertable()
     */
    public boolean isAutoInsertable() {
        return true;
    }

}