org.eclipse.wst.xml.ui.internal.contentassist.XMLTemplateCompletionProcessor.java Source code

Java tutorial

Introduction

Here is the source code for org.eclipse.wst.xml.ui.internal.contentassist.XMLTemplateCompletionProcessor.java

Source

/*******************************************************************************
 * Copyright (c) 2001, 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
 *     Jens Lukowski/Innoopract - initial renaming/restructuring
 *     gavingui2011@gmail.com - Add TapestryTools features
 *     
 *******************************************************************************/
package org.eclipse.wst.xml.ui.internal.contentassist;

import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.List;

import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.IProject;
import org.eclipse.jdt.core.IJavaElement;
import org.eclipse.jdt.core.IPackageFragment;
import org.eclipse.jdt.core.IPackageFragmentRoot;
import org.eclipse.jdt.core.JavaModelException;
import org.eclipse.jdt.internal.compiler.classfmt.ClassFormatException;
import org.eclipse.jdt.internal.core.ClassFile;
import org.eclipse.jdt.internal.core.PackageFragment;
import org.eclipse.jface.text.IDocument;
import org.eclipse.jface.text.IRegion;
import org.eclipse.jface.text.ITextSelection;
import org.eclipse.jface.text.ITextViewer;
import org.eclipse.jface.text.Region;
import org.eclipse.jface.text.contentassist.ICompletionProposal;
import org.eclipse.jface.text.templates.ContextTypeRegistry;
import org.eclipse.jface.text.templates.Template;
import org.eclipse.jface.text.templates.TemplateCompletionProcessor;
import org.eclipse.jface.text.templates.TemplateContext;
import org.eclipse.jface.text.templates.TemplateContextType;
import org.eclipse.jface.text.templates.TemplateException;
import org.eclipse.jface.text.templates.TemplateProposal;
import org.eclipse.jface.text.templates.persistence.TemplateStore;
import org.eclipse.swt.graphics.Image;
import org.eclipse.ui.IEditorPart;
import org.eclipse.ui.IFileEditorInput;
import org.eclipse.ui.internal.Workbench;
import org.eclipse.wst.sse.core.internal.provisional.IndexedRegion;
import org.eclipse.wst.sse.ui.internal.contentassist.ContentAssistUtils;
import org.eclipse.wst.xml.core.internal.contentmodel.tapestry.TapestryElementCollection;
import org.eclipse.wst.xml.core.internal.contentmodel.tapestry.travelpackage.CoreComponentsUtil;
import org.eclipse.wst.xml.core.internal.contentmodel.tapestry.travelpackage.TapestryClassLoader;
import org.eclipse.wst.xml.core.internal.contentmodel.tapestry.travelpackage.TapestryCoreComponents;
import org.eclipse.wst.xml.core.internal.provisional.document.IDOMNode;
import org.eclipse.wst.xml.ui.internal.XMLUIPlugin;
import org.eclipse.wst.xml.ui.internal.contentassist.tapestry.TapestryComponentCompletionProposalComputer;
import org.eclipse.wst.xml.ui.internal.contentassist.tapestry.TapestryRootComponentsProposalComputer;
import org.eclipse.wst.xml.ui.internal.editor.XMLEditorPluginImageHelper;
import org.eclipse.wst.xml.ui.internal.editor.XMLEditorPluginImages;
import org.w3c.dom.NamedNodeMap;
import org.w3c.dom.Node;

/**
 * <p>Completion computer for XML templates</p>
 */
class XMLTemplateCompletionProcessor extends TemplateCompletionProcessor {
    //Represent Tapestry component when try to pop up attributes list in components
    private Node currentTapestryComponent = null;
    private TapestryElementCollection collection = new TapestryElementCollection();
    private HashMap<String, TapestryCoreComponents[]> templateCacheMap = new HashMap<String, TapestryCoreComponents[]>();
    private TapestryClassLoader tapestryClassLoader = new TapestryClassLoader();
    private TapestryRootComponentsProposalComputer tapestryRootComponentsProposalComputer = new TapestryRootComponentsProposalComputer();

    private static final class ProposalComparator implements Comparator {
        public int compare(Object o1, Object o2) {
            return ((TemplateProposal) o2).getRelevance() - ((TemplateProposal) o1).getRelevance();
        }
    }

    private static final Comparator fgProposalComparator = new ProposalComparator();
    private String fContextTypeId = null;

    /**
     * This method is used to generate content assit for:
     * 1. blank space to list Tapestry components
     * 2. input '<' to list Tapestry components
     * 3. int '<t:' to list Tapestry components
     * 4. list attributes for Tapestry component
     * 5. list attributes values
     */
    public ICompletionProposal[] computeCompletionProposals(ITextViewer viewer, int offset) {

        ITextSelection selection = (ITextSelection) viewer.getSelectionProvider().getSelection();

        // adjust offset to end of normalized selection
        if (selection.getOffset() == offset) {
            offset = selection.getOffset() + selection.getLength();
        }

        String prefix = extractPrefix(viewer, offset);
        Region region = new Region(offset - prefix.length(), prefix.length());
        TemplateContext context = createContext(viewer, region, offset);
        if (context == null) {
            return new ICompletionProposal[0];
        }

        IndexedRegion treeNode = ContentAssistUtils.getNodeAt(viewer, offset);

        currentTapestryComponent = (Node) treeNode;
        char preChar = 0, preChar2 = 0;
        //In situation user input <, we should store the char before cursor into preChar and even preChar2
        if (currentTapestryComponent.getNodeValue() != null) {
            for (int i = offset - treeNode.getStartOffset() - 1; i >= 0; i--) {
                char temp = currentTapestryComponent.getNodeValue().charAt(i);
                if (temp != 9 && temp != 10 && temp != 32) {
                    if (preChar == 0)
                        preChar = temp;
                    else {
                        preChar2 = temp;
                        break;
                    }
                }
            }
        }

        while ((currentTapestryComponent != null) && (currentTapestryComponent.getNodeType() == Node.TEXT_NODE)
                && (currentTapestryComponent.getParentNode() != null)) {
            currentTapestryComponent = currentTapestryComponent.getParentNode();
        }

        // name of the selection variables {line, word}_selection
        context.setVariable("selection", selection.getText()); //$NON-NLS-1$

        System.out.println(">>>>> Get template list by context id:" + context.getContextType().getId()
                + "  selection:" + selection.getText());

        Template[] templates = getTemplates((IDOMNode) treeNode, offset, context.getContextType().getId(), preChar,
                preChar2);

        List matches = new ArrayList();
        for (int i = 0; i < templates.length; i++) {
            Template template = templates[i];
            try {
                context.getContextType().validate(template.getPattern());
            } catch (TemplateException e) {
                continue;
            }
            if (template.matches(prefix, context.getContextType().getId())) {
                int place = getRelevance(template, prefix);
                matches.add(createProposal(template, context, (IRegion) region, place));
            }
        }

        Collections.sort(matches, fgProposalComparator);

        return (ICompletionProposal[]) matches.toArray(new ICompletionProposal[matches.size()]);
    }

    /**
     * Creates a concrete template context for the given region in the
     * document. This involves finding out which context type is valid at the
     * given location, and then creating a context of this type. The default
     * implementation returns a <code>SmartReplaceTemplateContext</code> for
     * the context type at the given location. This takes the offset at which
     * content assist was invoked into consideration.
     * 
     * @param viewer
     *            the viewer for which the context is created
     * @param region
     *            the region into <code>document</code> for which the
     *            context is created
     * @param offset
     *            the original offset where content assist was invoked
     * @return a template context that can handle template insertion at the
     *         given location, or <code>null</code>
     */
    private TemplateContext createContext(ITextViewer viewer, IRegion region, int offset) {
        // pretty much same code as super.createContext except create
        // SmartReplaceTemplateContext
        TemplateContextType contextType = getContextType(viewer, region);
        if (contextType != null) {
            IDocument document = viewer.getDocument();
            return new ReplaceNameTemplateContext(contextType, document, region.getOffset(), region.getLength(),
                    offset);
        }
        return null;
    }

    protected ICompletionProposal createProposal(Template template, TemplateContext context, IRegion region,
            int relevance) {
        return new CustomTemplateProposal(template, context, region, getImage(template), relevance);
    }

    protected TemplateContextType getContextType(ITextViewer viewer, IRegion region) {
        TemplateContextType type = null;

        ContextTypeRegistry registry = getTemplateContextRegistry();
        if (registry != null) {
            type = registry.getContextType(fContextTypeId);
        }

        return type;
    }

    protected Image getImage(Template template) {
        if (template.getContextTypeId().equals(TapestryElementCollection.componentsContextTypeId))
            return XMLEditorPluginImageHelper.getInstance().getImage(XMLEditorPluginImages.IMG_TAPESTRY_DEFAULT);
        else if (template.getContextTypeId().equals(TapestryElementCollection.attributesContextTypeId))
            return XMLEditorPluginImageHelper.getInstance().getImage(XMLEditorPluginImages.IMG_TAPESTRY_ATTRIBUTE);
        else if (template.getContextTypeId().equals(TapestryElementCollection.entitiesContextTypeId))
            return XMLEditorPluginImageHelper.getInstance().getImage(XMLEditorPluginImages.IMG_TAPESTRY_ENTITY);
        else if (template.getContextTypeId().equals(TapestryElementCollection.attributesValueContextTypeId))
            return XMLEditorPluginImageHelper.getInstance().getImage(XMLEditorPluginImages.IMG_TAPESTRY_DEFAULT);
        else
            return XMLEditorPluginImageHelper.getInstance().getImage(XMLEditorPluginImages.IMG_OBJ_TAG_MACRO);
    }

    private ContextTypeRegistry getTemplateContextRegistry() {
        return XMLUIPlugin.getDefault().getTemplateContextRegistry();
    }

    /**
     * This method does not used, just implement abstract method
     */
    protected Template[] getTemplates(String contextTypeId) {

        return null;
    }

    private IProject getCurrentProject() {
        IEditorPart editorPart = Workbench.getInstance().getActiveWorkbenchWindow().getActivePage()
                .getActiveEditor();
        IFileEditorInput input = (IFileEditorInput) editorPart.getEditorInput();
        IFile file = input.getFile();
        return file.getProject();
    }

    private PackageFragment getTapestryCoreLibrary() {
        IPackageFragmentRoot root = tapestryClassLoader.getTapestryCoreJar(getCurrentProject());
        if (root == null)
            return null;
        IPackageFragment pack = root.getPackageFragment("org.apache.tapestry5.corelib.components");
        if (pack != null && pack instanceof PackageFragment)
            return (PackageFragment) pack;
        return null;
    }

    /**
     * Get templates entrance method
     * 
     * @param node
     * @param offset
     * @param contextTypeId
     * @param preChar
     * @param preChar2
     * @return
     */
    protected Template[] getTemplates(IDOMNode node, int offset, String contextTypeId, char preChar,
            char preChar2) {
        IProject project = getCurrentProject();
        String mapKey = project.getName();
        TapestryCoreComponents[] coreList = this.templateCacheMap.get(mapKey);
        if (coreList == null || coreList.length <= 0) {
            //Get tapestry components from classpath
            List<TapestryCoreComponents> list = new ArrayList<TapestryCoreComponents>();
            PackageFragment tapestryCorePackage = getTapestryCoreLibrary();
            try {
                if (tapestryCorePackage != null)
                    for (Object packo : tapestryCorePackage.getChildrenOfType(IJavaElement.CLASS_FILE)) {
                        ClassFile packi = (ClassFile) packo;
                        if (packi.getElementName().indexOf('$') < 0) {
                            TapestryCoreComponents component = tapestryClassLoader
                                    .loadComponentAttributesFromClassFile(
                                            tapestryClassLoader.getTapestryCoreJar(project), "t", packi);
                            if (component != null)
                                list.add(component);
                        }
                    }
            } catch (JavaModelException e) {
                e.printStackTrace();
            } catch (ClassFormatException e) {
                e.printStackTrace();
            }
            if (list != null && list.size() > 0) {
                coreList = list.toArray(new TapestryCoreComponents[0]);
                this.templateCacheMap.put(mapKey, coreList);
            }
        }

        if (coreList == null)
            return new Template[0];

        if (contextTypeId.equals(TapestryElementCollection.componentsContextTypeId)) {
            boolean customComponent = false;
            int type = 1;
            if (preChar == '<')
                type = 2;
            else if (currentTapestryComponent.getPrefix() != null) {
                customComponent = tapestryRootComponentsProposalComputer
                        .getComponentsPrefixList(this.getCurrentProject())
                        .contains(currentTapestryComponent.getPrefix());
                if (customComponent)
                    type = 3;
            }
            List<Template> components = new ArrayList<Template>();
            if (currentTapestryComponent.getPrefix() != null && !customComponent)
                return components.toArray(new Template[0]);

            if (type != 3 || currentTapestryComponent.getNodeName().equals("t:")) {
                List<Template> buildInList = CoreComponentsUtil.buildTemplateListFromComponents(coreList,
                        contextTypeId, type);
                if (buildInList != null && buildInList.size() > 0)
                    components.addAll(buildInList);
                List<Template> rootComponents = tapestryRootComponentsProposalComputer
                        .getRootComponentsTemplates(this.getCurrentProject(), contextTypeId, type);
                if (rootComponents != null && rootComponents.size() > 0)
                    components.addAll(rootComponents);
            }

            if (currentTapestryComponent.getPrefix() == null || (currentTapestryComponent.getPrefix() + ":")
                    .equals(currentTapestryComponent.getNodeName())) {
                List<Template> customComponents = tapestryRootComponentsProposalComputer
                        .getCustomComponentsTemplates(this.getCurrentProject(), contextTypeId, type,
                                currentTapestryComponent.getPrefix());
                if (customComponents != null && customComponents.size() > 0)
                    components.addAll(customComponents);
            }

            return components == null ? null : components.toArray(new Template[0]);
        } else if (contextTypeId.equals(TapestryElementCollection.attributesContextTypeId)) {
            String tapestryComponentName = getTapestryComponentName(node);
            //In condition <t:ActionLink
            if (tapestryComponentName == null)
                tapestryComponentName = currentTapestryComponent.getNodeName().toLowerCase();
            //In condition <t:html.Message
            if (tapestryComponentName.indexOf('.') > -1 && currentTapestryComponent.getPrefix() != null
                    && currentTapestryComponent.getPrefix().equals("t"))
                tapestryComponentName = tapestryComponentName.substring(2).replace('.', ':');

            List<Template> tapestryTemplates = CoreComponentsUtil.getAttributeList(coreList, contextTypeId,
                    tapestryComponentName);
            if (tapestryTemplates == null || tapestryTemplates.size() == 0)
                tapestryTemplates = tapestryRootComponentsProposalComputer.getRootComponentsAttributes(project,
                        contextTypeId, tapestryComponentName);
            if (tapestryTemplates == null || tapestryTemplates.size() == 0)
                tapestryTemplates = tapestryRootComponentsProposalComputer.getCustomComponentsAttributes(project,
                        contextTypeId, tapestryComponentName, tapestryClassLoader);

            return tapestryTemplates == null ? null : tapestryTemplates.toArray(new Template[0]);
        } else if (contextTypeId.equals(TapestryElementCollection.attributesValueContextTypeId)) {
            List<Template> tapestryTemplates = null;
            if (isComponentTypeContentAssist(node, offset)) {
                tapestryTemplates = CoreComponentsUtil.getTapestryComponentNameList(coreList, contextTypeId);
                List<Template> rootComponents = tapestryRootComponentsProposalComputer
                        .getRootComponentsNameTemplates(this.getCurrentProject(), contextTypeId);
                if (rootComponents != null && rootComponents.size() > 0)
                    tapestryTemplates.addAll(rootComponents);
                List<Template> customComponents = tapestryRootComponentsProposalComputer
                        .getCustomComponentsNameTemplates(this.getCurrentProject(), contextTypeId);
                if (customComponents != null && customComponents.size() > 0)
                    tapestryTemplates.addAll(customComponents);
            } else if (isComponentContentassist(node, offset)) {
                tapestryTemplates = TapestryComponentCompletionProposalComputer.getInstance()
                        .computeCompletionProposals("", node, offset);
            } else {
                tapestryTemplates = collection.getAttributeValueList(contextTypeId, currentTapestryComponent);
            }
            return tapestryTemplates == null ? null : tapestryTemplates.toArray(new Template[0]);
        } else {
            Template templates[] = null;
            TemplateStore store = getTemplateStore();
            if (store != null) {
                templates = store.getTemplates(contextTypeId);
            }

            return templates;
        }
    }

    /**
     * Get component name such as "ActionLink" in this condition <a t:type="ActionLink"></a>
     * 
     * @param node
     * @return
     */
    private String getTapestryComponentName(IDOMNode node) {
        NamedNodeMap attributes = node.getAttributes();
        if (attributes != null && attributes.getLength() > 0) {
            for (int i = 0; i < attributes.getLength(); i++) {
                if (attributes.item(i).getNodeName().equals("t:type")) {
                    String value = attributes.item(i).getNodeValue();
                    if (value.indexOf('/') > -1) {
                        return value.replace('/', ':').toLowerCase();
                    } else
                        return "t:" + value.toLowerCase();
                }
            }
        }
        return null;
    }

    //Decide whether in condition <span t:id="
    private boolean isComponentContentassist(IDOMNode node, int offset) {
        int sp = 0, ep = 0;
        for (int i = offset - node.getStartOffset() - 1; i >= 0; i--) {
            char temp = node.getSource().charAt(i);
            if (ep == 0 && temp == '=')
                ep = i;
            else if (sp == 0 && temp == ' ')
                sp = i;
            if (sp != 0 && ep != 0)
                break;
        }
        if (sp == 0 || ep == 0)
            return false;
        else {
            if (node.getSource().substring(sp, ep).trim().equals("t:id"))
                return true;
            else
                return false;
        }
    }

    //Decide whether in condition <a t:type=""></a>
    private boolean isComponentTypeContentAssist(IDOMNode node, int offset) {
        int sp = 0, ep = 0;
        for (int i = offset - node.getStartOffset() - 1; i >= 0; i--) {
            char temp = node.getSource().charAt(i);
            if (ep == 0 && temp == '=')
                ep = i;
            else if (sp == 0 && temp == ' ')
                sp = i;
            if (sp != 0 && ep != 0)
                break;
        }
        if (sp == 0 || ep == 0)
            return false;
        else {
            if (node.getSource().substring(sp, ep).trim().equals("t:type"))
                return true;
            else
                return false;
        }
    }

    private TemplateStore getTemplateStore() {
        return XMLUIPlugin.getDefault().getTemplateStore();
    }

    void setContextType(String contextTypeId) {
        fContextTypeId = contextTypeId;
    }

}