Java tutorial
/******************************************************************************* * 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; } }