| /******************************************************************************* |
| * Copyright (c) 2002, 2021 GEBIT Gesellschaft fuer EDV-Beratung |
| * und Informatik-Technologien mbH, |
| * Berlin, Duesseldorf, Frankfurt (Germany) and others. |
| * |
| * This program and the accompanying materials |
| * are made available under the terms of the Eclipse Public License 2.0 |
| * which accompanies this distribution, and is available at |
| * https://www.eclipse.org/legal/epl-2.0/ |
| * |
| * SPDX-License-Identifier: EPL-2.0 |
| * |
| * Contributors: |
| * GEBIT Gesellschaft fuer EDV-Beratung und Informatik-Technologien mbH - initial API and implementation |
| * IBM Corporation - bug fixes |
| * John-Mason P. Shackelford (john-mason.shackelford@pearson.com) - bug 49383, 56299, 59024 |
| * Brock Janiczak (brockj_eclipse@ihug.com.au ) - bug 78028, 78030 |
| * Remy Chi Jian Suen - bug 277587 |
| *******************************************************************************/ |
| |
| package org.eclipse.ant.internal.ui.editor; |
| |
| import java.io.File; |
| import java.io.IOException; |
| import java.io.InputStream; |
| import java.io.InputStreamReader; |
| import java.io.Reader; |
| import java.lang.reflect.InvocationTargetException; |
| import java.text.MessageFormat; |
| import java.util.ArrayList; |
| import java.util.Arrays; |
| import java.util.Collections; |
| import java.util.Comparator; |
| import java.util.Enumeration; |
| import java.util.HashMap; |
| import java.util.Iterator; |
| import java.util.List; |
| import java.util.Map; |
| import java.util.Map.Entry; |
| import java.util.Set; |
| |
| import org.apache.tools.ant.AntTypeDefinition; |
| import org.apache.tools.ant.BuildException; |
| import org.apache.tools.ant.ComponentHelper; |
| import org.apache.tools.ant.ExtensionPoint; |
| import org.apache.tools.ant.IntrospectionHelper; |
| import org.apache.tools.ant.Project; |
| import org.apache.tools.ant.Target; |
| import org.apache.tools.ant.taskdefs.MacroDef; |
| import org.apache.tools.ant.taskdefs.MacroDef.Attribute; |
| import org.apache.tools.ant.taskdefs.MacroDef.TemplateElement; |
| import org.apache.tools.ant.taskdefs.MacroInstance; |
| import org.apache.tools.ant.types.EnumeratedAttribute; |
| import org.apache.tools.ant.types.Reference; |
| import org.eclipse.ant.internal.core.IAntCoreConstants; |
| import org.eclipse.ant.internal.ui.AntUIImages; |
| import org.eclipse.ant.internal.ui.AntUIPlugin; |
| import org.eclipse.ant.internal.ui.IAntUIConstants; |
| import org.eclipse.ant.internal.ui.dtd.IAttribute; |
| import org.eclipse.ant.internal.ui.dtd.IDfm; |
| import org.eclipse.ant.internal.ui.dtd.IElement; |
| import org.eclipse.ant.internal.ui.dtd.ISchema; |
| import org.eclipse.ant.internal.ui.dtd.ParseError; |
| import org.eclipse.ant.internal.ui.dtd.Parser; |
| import org.eclipse.ant.internal.ui.editor.TaskDescriptionProvider.ProposalNode; |
| import org.eclipse.ant.internal.ui.editor.templates.AntContext; |
| import org.eclipse.ant.internal.ui.editor.templates.AntTemplateAccess; |
| import org.eclipse.ant.internal.ui.editor.templates.AntTemplateInformationControlCreator; |
| import org.eclipse.ant.internal.ui.editor.templates.AntTemplateProposal; |
| import org.eclipse.ant.internal.ui.editor.templates.BuildFileContextType; |
| import org.eclipse.ant.internal.ui.editor.templates.TargetContextType; |
| import org.eclipse.ant.internal.ui.editor.templates.TaskContextType; |
| import org.eclipse.ant.internal.ui.model.AntDefiningTaskNode; |
| import org.eclipse.ant.internal.ui.model.AntElementNode; |
| import org.eclipse.ant.internal.ui.model.AntModel; |
| import org.eclipse.ant.internal.ui.model.AntProjectNode; |
| import org.eclipse.ant.internal.ui.model.AntTargetNode; |
| import org.eclipse.ant.internal.ui.model.AntTaskNode; |
| import org.eclipse.ant.internal.ui.model.IAntElement; |
| import org.eclipse.core.commands.Command; |
| import org.eclipse.core.commands.ParameterizedCommand; |
| import org.eclipse.jface.bindings.TriggerSequence; |
| import org.eclipse.jface.operation.IRunnableWithProgress; |
| import org.eclipse.jface.text.BadLocationException; |
| 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.Position; |
| import org.eclipse.jface.text.Region; |
| import org.eclipse.jface.text.contentassist.ContentAssistEvent; |
| import org.eclipse.jface.text.contentassist.ICompletionListener; |
| import org.eclipse.jface.text.contentassist.ICompletionProposal; |
| import org.eclipse.jface.text.contentassist.IContentAssistant; |
| import org.eclipse.jface.text.contentassist.IContentAssistantExtension2; |
| import org.eclipse.jface.text.contentassist.IContextInformation; |
| import org.eclipse.jface.text.contentassist.IContextInformationValidator; |
| 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.swt.graphics.Image; |
| import org.eclipse.swt.graphics.Point; |
| import org.eclipse.ui.IEditorPart; |
| import org.eclipse.ui.IWorkbenchPage; |
| import org.eclipse.ui.PlatformUI; |
| import org.eclipse.ui.commands.ICommandService; |
| import org.eclipse.ui.keys.IBindingService; |
| import org.eclipse.ui.part.FileEditorInput; |
| import org.eclipse.ui.progress.IProgressService; |
| import org.eclipse.ui.texteditor.ITextEditorActionDefinitionIds; |
| import org.w3c.dom.Element; |
| import org.w3c.dom.Node; |
| import org.w3c.dom.NodeList; |
| |
| /** |
| * The completion processor for the Ant Editor. |
| */ |
| public class AntEditorCompletionProcessor extends TemplateCompletionProcessor implements ICompletionListener { |
| |
| private Comparator<ICompletionProposal> proposalComparator = new Comparator<>() { |
| @Override |
| public int compare(ICompletionProposal o1, ICompletionProposal o2) { |
| |
| int type1 = getProposalType(o1); |
| int type2 = getProposalType(o2); |
| if (type1 != type2) { |
| if (type1 > type2) { |
| return 1; |
| } |
| return -1; |
| } |
| String string1 = o1.getDisplayString(); |
| String string2 = o2.getDisplayString(); |
| return string1.compareToIgnoreCase(string2); |
| } |
| |
| private int getProposalType(Object o) { |
| if (o instanceof AntCompletionProposal) { |
| return ((AntCompletionProposal) o).getType(); |
| } |
| return AntCompletionProposal.TASK_PROPOSAL; |
| } |
| }; |
| |
| protected final static int PROPOSAL_MODE_NONE = 0; |
| protected final static int PROPOSAL_MODE_BUILDFILE = 1; |
| protected final static int PROPOSAL_MODE_TASK_PROPOSAL = 2; |
| protected final static int PROPOSAL_MODE_PROPERTY_PROPOSAL = 3; |
| protected final static int PROPOSAL_MODE_ATTRIBUTE_PROPOSAL = 4; |
| protected final static int PROPOSAL_MODE_TASK_PROPOSAL_CLOSING = 5; |
| protected final static int PROPOSAL_MODE_ATTRIBUTE_VALUE_PROPOSAL = 6; |
| protected final static int PROPOSAL_MODE_NESTED_ELEMENT_PROPOSAL = 7; |
| |
| private final static ICompletionProposal[] NO_PROPOSALS = new ICompletionProposal[0]; |
| |
| /** |
| * The fully qualified name of the {@link MacroInstance} class |
| * |
| * @since 3.5.500 |
| */ |
| private static final String MACROINSTANCE_NAME = "org.apache.tools.ant.taskdefs.MacroInstance"; //$NON-NLS-1$ |
| |
| /** |
| * The line where the cursor sits now. |
| * <P> |
| * The first line has index '1'. |
| */ |
| protected int lineNumber = -1; |
| |
| /** |
| * The startingColumn where the cursor sits now. |
| * <P> |
| * The first startingColumn has index '1'. |
| */ |
| protected int columnNumber = -1; |
| |
| /** |
| * The additional offset required from a required attribute to place the cursor for the current proposal |
| */ |
| private int additionalProposalOffset = -1; |
| |
| private static final String ANT_DTD_FILENAME = "/org/eclipse/ant/internal/ui/editor/ant1.6.2.dtd"; //$NON-NLS-1$ |
| |
| /** |
| * The DTD. |
| */ |
| private static ISchema fgDtd; |
| |
| /** |
| * Cursor position, counted from the beginning of the document. |
| * <P> |
| * The first position has index '0'. |
| */ |
| protected int cursorPosition = -1; |
| |
| /** |
| * The text viewer. |
| */ |
| private ITextViewer viewer; |
| |
| /** |
| * The set of characters that will trigger the activation of the completion proposal computation. |
| */ |
| private char[] autoActivationChars = null; |
| |
| private String errorMessage; |
| |
| protected AntModel antModel; |
| |
| /** |
| * The proposal mode for the current content assist |
| * |
| * @see #determineProposalMode(IDocument, int, String) |
| */ |
| private int currentProposalMode = -1; |
| |
| /** |
| * The prefix for the current content assist |
| */ |
| protected String currentPrefix = null; |
| |
| /** |
| * The current task string for content assist |
| * |
| * @see #determineProposalMode(IDocument, int, String) |
| */ |
| protected String currentTaskString = null; |
| |
| private boolean fTemplatesOnly = false; |
| protected IContentAssistantExtension2 fContentAssistant; |
| |
| public AntEditorCompletionProcessor(AntModel model) { |
| super(); |
| antModel = model; |
| } |
| |
| /** |
| * Parses the dtd. |
| */ |
| private ISchema parseDtd() throws ParseError, IOException { |
| try (InputStream stream = getClass().getResourceAsStream(ANT_DTD_FILENAME); Reader reader = new InputStreamReader(stream, "UTF-8");) {//$NON-NLS-1$ |
| Parser parser = new Parser(); |
| ISchema schema = parser.parseDTD(reader, "project"); //$NON-NLS-1$ |
| return schema; |
| } |
| } |
| |
| @Override |
| public ICompletionProposal[] computeCompletionProposals(ITextViewer refViewer, int documentOffset) { |
| this.viewer = refViewer; |
| try { |
| if (fTemplatesOnly) { |
| fContentAssistant.setStatusMessage(getIterationGestureMessage(AntEditorMessages.getString("AntEditorCompletionProcessor.0"))); //$NON-NLS-1$ |
| fContentAssistant.setEmptyMessage(AntEditorMessages.getString("AntEditorCompletionProcessor.60")); //$NON-NLS-1$ |
| ICompletionProposal[] templates = determineTemplateProposals(refViewer, documentOffset); |
| Arrays.sort(templates, proposalComparator); |
| return templates; |
| } |
| fContentAssistant.setStatusMessage(getIterationGestureMessage(AntEditorMessages.getString("AntEditorCompletionProcessor.61"))); //$NON-NLS-1$ |
| fContentAssistant.setEmptyMessage(AntEditorMessages.getString("AntEditorCompletionProcessor.62")); //$NON-NLS-1$ |
| ICompletionProposal[] matchingProposals = determineProposals(); |
| ICompletionProposal[] matchingTemplateProposals = determineTemplateProposals(refViewer, documentOffset); |
| return mergeProposals(matchingProposals, matchingTemplateProposals); |
| } |
| finally { |
| currentPrefix = null; |
| currentProposalMode = -1; |
| fTemplatesOnly = !fTemplatesOnly; |
| } |
| } |
| |
| protected ICompletionProposal[] determineTemplateProposals(ITextViewer refViewer, int documentOffset) { |
| this.viewer = refViewer; |
| String prefix = getCurrentPrefix(); |
| ICompletionProposal[] matchingTemplateProposals; |
| if (prefix.length() == 0) { |
| matchingTemplateProposals = determineTemplateProposalsForContext(documentOffset); |
| } else { |
| ICompletionProposal[] templateProposals = determineTemplateProposalsForContext(documentOffset); |
| List<ICompletionProposal> templateProposalList = new ArrayList<>(templateProposals.length); |
| for (ICompletionProposal templateProposal : templateProposals) { |
| if (templateProposal.getDisplayString().toLowerCase().startsWith(prefix)) { |
| templateProposalList.add(templateProposal); |
| } |
| } |
| matchingTemplateProposals = templateProposalList.toArray(new ICompletionProposal[templateProposalList.size()]); |
| } |
| return matchingTemplateProposals; |
| } |
| |
| // essentially a copy of super.computeCompletionProposals but we need to have both context types work |
| // for target (task and target) context type in a backwards compatible way |
| private ICompletionProposal[] determineTemplateProposalsForContext(int offset) { |
| ITextSelection selection = (ITextSelection) viewer.getSelectionProvider().getSelection(); |
| |
| // adjust offset to end of normalized selection |
| int newoffset = offset; |
| if (selection.getOffset() == newoffset) { |
| newoffset = selection.getOffset() + selection.getLength(); |
| } |
| |
| String prefix = extractPrefix(viewer, newoffset); |
| Region region = new Region(newoffset - prefix.length(), prefix.length()); |
| TemplateContext context = createContext(viewer, region); |
| if (context == null) { |
| return new ICompletionProposal[0]; |
| } |
| |
| context.setVariable("selection", selection.getText()); // name of the selection variables {line, word}_selection //$NON-NLS-1$ |
| |
| Template[] templates; |
| String contextTypeId = context.getContextType().getId(); |
| boolean isTargetContextType = contextTypeId.equals(TargetContextType.TARGET_CONTEXT_TYPE); |
| if (isTargetContextType) { |
| Template[] tasks = AntTemplateAccess.getDefault().getTemplateStore().getTemplates(TaskContextType.TASK_CONTEXT_TYPE); |
| Template[] targets = getTemplates(contextTypeId); |
| templates = new Template[tasks.length + targets.length]; |
| System.arraycopy(tasks, 0, templates, 0, tasks.length); |
| System.arraycopy(targets, 0, templates, tasks.length, targets.length); |
| } else { |
| templates = getTemplates(contextTypeId); |
| } |
| |
| List<ICompletionProposal> matches = new ArrayList<>(); |
| for (Template template : templates) { |
| try { |
| context.getContextType().validate(template.getPattern()); |
| } |
| catch (TemplateException e) { |
| continue; |
| } |
| if (template.matches(prefix, contextTypeId) || (isTargetContextType && template.matches(prefix, TaskContextType.TASK_CONTEXT_TYPE))) { |
| matches.add(createProposal(template, context, (IRegion) region, getRelevance(template, prefix))); |
| } |
| } |
| |
| Collections.sort(matches, proposalComparator); |
| |
| return matches.toArray(new ICompletionProposal[matches.size()]); |
| } |
| |
| private ICompletionProposal[] mergeProposals(ICompletionProposal[] proposals1, ICompletionProposal[] proposals2) { |
| |
| ICompletionProposal[] combinedProposals = new ICompletionProposal[proposals1.length + proposals2.length]; |
| |
| System.arraycopy(proposals1, 0, combinedProposals, 0, proposals1.length); |
| System.arraycopy(proposals2, 0, combinedProposals, proposals1.length, proposals2.length); |
| |
| Arrays.sort(combinedProposals, proposalComparator); |
| return combinedProposals; |
| } |
| |
| @Override |
| public IContextInformation[] computeContextInformation(ITextViewer refViewer, int documentOffset) { |
| return new IContextInformation[0]; |
| } |
| |
| @Override |
| public char[] getCompletionProposalAutoActivationCharacters() { |
| return autoActivationChars; |
| } |
| |
| @Override |
| public char[] getContextInformationAutoActivationCharacters() { |
| return null; |
| } |
| |
| @Override |
| public IContextInformationValidator getContextInformationValidator() { |
| return null; |
| } |
| |
| @Override |
| public String getErrorMessage() { |
| return errorMessage; |
| } |
| |
| /** |
| * Returns the new determined proposals. |
| */ |
| private ICompletionProposal[] determineProposals() { |
| ITextSelection selection = (ITextSelection) viewer.getSelectionProvider().getSelection(); |
| cursorPosition = selection.getOffset() + selection.getLength(); |
| |
| IDocument doc = viewer.getDocument(); |
| try { |
| lineNumber = doc.getLineOfOffset(cursorPosition); |
| columnNumber = cursorPosition - doc.getLineOffset(lineNumber); |
| } |
| catch (BadLocationException e) { |
| AntUIPlugin.log(e); |
| } |
| |
| String prefix = getCurrentPrefix(); |
| if (prefix == null || cursorPosition == -1) { |
| return NO_PROPOSALS; |
| } |
| |
| ICompletionProposal[] proposals = getProposalsFromDocument(doc, prefix); |
| currentTaskString = null; |
| return proposals; |
| } |
| |
| /** |
| * Returns the proposals for the specified document. |
| */ |
| protected ICompletionProposal[] getProposalsFromDocument(IDocument document, String prefix) { |
| ICompletionProposal[] proposals = null; |
| currentProposalMode = determineProposalMode(document, cursorPosition, prefix); |
| switch (currentProposalMode) { |
| case PROPOSAL_MODE_ATTRIBUTE_PROPOSAL: |
| proposals = getAttributeProposals(currentTaskString, prefix); |
| if (proposals.length == 0) { |
| errorMessage = AntEditorMessages.getString("AntEditorCompletionProcessor.28"); //$NON-NLS-1$ |
| } |
| break; |
| case PROPOSAL_MODE_TASK_PROPOSAL: |
| String parentName = getParentName(document, lineNumber, columnNumber); |
| if (parentName == null || parentName.length() == 0) { // outside of any parent element |
| proposals = NO_PROPOSALS; |
| currentProposalMode = PROPOSAL_MODE_NONE; |
| } else { |
| proposals = getTaskProposals(document, parentName, prefix); |
| } |
| if (proposals.length == 0) { |
| errorMessage = AntEditorMessages.getString("AntEditorCompletionProcessor.29"); //$NON-NLS-1$ |
| } |
| break; |
| case PROPOSAL_MODE_BUILDFILE: |
| proposals = getBuildFileProposals(document, prefix); |
| if (proposals.length == 0) { |
| errorMessage = AntEditorMessages.getString("AntEditorCompletionProcessor.29"); //$NON-NLS-1$ |
| } |
| break; |
| case PROPOSAL_MODE_TASK_PROPOSAL_CLOSING: |
| ICompletionProposal proposal = getClosingTaskProposal(getOpenElementName(), prefix, true); |
| if (proposal == null) { |
| errorMessage = AntEditorMessages.getString("AntEditorCompletionProcessor.30"); //$NON-NLS-1$ |
| proposals = NO_PROPOSALS; |
| } else { |
| proposals = new ICompletionProposal[] { proposal }; |
| } |
| break; |
| case PROPOSAL_MODE_ATTRIBUTE_VALUE_PROPOSAL: |
| String textToSearch = document.get().substring(0, cursorPosition - prefix.length()); |
| String attributeString = getAttributeStringFromDocumentStringToPrefix(textToSearch); |
| if ("target".equalsIgnoreCase(currentTaskString) || "extension-point".equalsIgnoreCase(currentTaskString)) { //$NON-NLS-1$ //$NON-NLS-2$ |
| proposals = getTargetAttributeValueProposals(document, textToSearch, prefix, attributeString); |
| } else if ("antcall".equalsIgnoreCase(currentTaskString)) { //$NON-NLS-1$ |
| proposals = getAntCallAttributeValueProposals(document, prefix, attributeString); |
| } else if ("project".equalsIgnoreCase(currentTaskString)) { //$NON-NLS-1$ |
| proposals = getProjectAttributeValueProposals(prefix, attributeString); |
| } else if ("refid".equalsIgnoreCase(attributeString) || "classpathref".equalsIgnoreCase(attributeString) //$NON-NLS-1$//$NON-NLS-2$ |
| || "sourcepathref".equalsIgnoreCase(attributeString) || "bootpathref".equalsIgnoreCase(attributeString)) { //$NON-NLS-1$ //$NON-NLS-2$ |
| proposals = getReferencesValueProposals(prefix); |
| } else { |
| proposals = getAttributeValueProposals(currentTaskString, attributeString, prefix); |
| } |
| if (proposals.length == 0) { |
| errorMessage = AntEditorMessages.getString("AntEditorCompletionProcessor.31"); //$NON-NLS-1$ |
| } |
| break; |
| case PROPOSAL_MODE_PROPERTY_PROPOSAL: |
| proposals = getPropertyProposals(document, prefix, cursorPosition); |
| if (proposals.length == 0) { |
| errorMessage = AntEditorMessages.getString("AntEditorCompletionProcessor.32"); //$NON-NLS-1$ |
| } |
| break; |
| case PROPOSAL_MODE_NONE: |
| default: |
| proposals = NO_PROPOSALS; |
| errorMessage = AntEditorMessages.getString("AntEditorCompletionProcessor.33"); //$NON-NLS-1$ |
| } |
| |
| if (proposals.length > 0) { |
| errorMessage = IAntCoreConstants.EMPTY_STRING; |
| } |
| return proposals; |
| |
| } |
| |
| private ICompletionProposal[] getProjectAttributeValueProposals(String prefix, String attributeName) { |
| if (attributeName.equalsIgnoreCase(IAntCoreConstants.DEFAULT)) { |
| return getDefaultValueProposals(prefix); |
| } |
| |
| return NO_PROPOSALS; |
| } |
| |
| private ICompletionProposal[] getDefaultValueProposals(String prefix) { |
| Map<String, Target> targets = getTargets(); |
| List<AntCompletionProposal> defaultProposals = new ArrayList<>(targets.size()); |
| Iterator<Target> itr = targets.values().iterator(); |
| Target target; |
| String targetName; |
| while (itr.hasNext()) { |
| target = itr.next(); |
| targetName = target.getName(); |
| if (targetName.toLowerCase().startsWith(prefix) && targetName.length() > 0) { |
| defaultProposals.add(new AntCompletionProposal(targetName, cursorPosition |
| - prefix.length(), prefix.length(), targetName.length(), getTargetImage(targetName), targetName, target.getDescription(), AntCompletionProposal.TASK_PROPOSAL)); |
| } |
| } |
| ICompletionProposal[] proposals = new ICompletionProposal[defaultProposals.size()]; |
| return defaultProposals.toArray(proposals); |
| } |
| |
| private ICompletionProposal[] getReferencesValueProposals(String prefix) { |
| Project project = antModel.getProjectNode().getProject(); |
| Map<String, Object> references = project.getReferences(); |
| if (references.isEmpty()) { |
| return NO_PROPOSALS; |
| } |
| |
| IAntElement node = antModel.getNode(cursorPosition, false); |
| if (node == null) { |
| return NO_PROPOSALS; |
| } |
| while (node.getParentNode() instanceof AntTaskNode) { |
| node = node.getParentNode(); |
| } |
| String id = null; |
| if (node instanceof AntTaskNode) { |
| id = ((AntTaskNode) node).getId(); |
| } |
| Set<String> refIds = references.keySet(); |
| List<ICompletionProposal> proposals = new ArrayList<>(refIds.size()); |
| String refId; |
| ICompletionProposal proposal; |
| int prefixLength = prefix.length(); |
| int replacementOffset = cursorPosition - prefixLength; |
| Iterator<String> iter = refIds.iterator(); |
| while (iter.hasNext()) { |
| refId = iter.next(); |
| if (!refId.equals(id) && (prefixLength == 0 || refId.toLowerCase().startsWith(prefix))) { |
| proposal = new AntCompletionProposal(refId, replacementOffset, prefixLength, refId.length(), null, refId, null, AntCompletionProposal.TASK_PROPOSAL); |
| proposals.add(proposal); |
| } |
| } |
| return proposals.toArray(new ICompletionProposal[proposals.size()]); |
| } |
| |
| protected ICompletionProposal[] getTargetAttributeValueProposals(IDocument document, String textToSearch, String prefix, String attributeName) { |
| if (attributeName.equalsIgnoreCase("depends")) { //$NON-NLS-1$ |
| return getDependsValueProposals(document, prefix); |
| } else if (attributeName.equalsIgnoreCase("if") || attributeName.equalsIgnoreCase("unless")) { //$NON-NLS-1$ //$NON-NLS-2$ |
| if (!textToSearch.trim().endsWith(",")) { //$NON-NLS-1$ |
| return getPropertyProposals(document, prefix, cursorPosition); |
| } |
| } else if (attributeName.equalsIgnoreCase("extensionOf")) {//$NON-NLS-1$ |
| return getExtensionOfValueProposals(prefix); |
| } |
| |
| return NO_PROPOSALS; |
| } |
| |
| private ICompletionProposal[] getExtensionOfValueProposals(String prefix) { |
| List<ICompletionProposal> extensions = new ArrayList<>(); |
| Map<String, Target> targets = getTargets(); |
| Set<String> targetNames = targets.keySet(); |
| for (String targetName : targetNames) { |
| Target currentTarget = targets.get(targetName); |
| if (currentTarget instanceof ExtensionPoint) { |
| extensions.add(new AntCompletionProposal(targetName, cursorPosition |
| - prefix.length(), prefix.length(), targetName.length(), getTargetImage(targetName), targetName, targets.get(targetName).getDescription(), AntCompletionProposal.TASK_PROPOSAL)); |
| } |
| |
| } |
| return extensions.toArray(new ICompletionProposal[extensions.size()]); |
| } |
| |
| protected ICompletionProposal[] getAntCallAttributeValueProposals(IDocument document, String prefix, String attributeName) { |
| if (attributeName.equalsIgnoreCase("target")) { //$NON-NLS-1$ |
| return getTargetProposals(document, prefix); |
| } |
| |
| return NO_PROPOSALS; |
| } |
| |
| private ICompletionProposal[] getTargetProposals(IDocument document, String prefix) { |
| String currentTargetName = getEnclosingTargetName(document, lineNumber, columnNumber); |
| if (currentTargetName == null) { |
| return NO_PROPOSALS; |
| } |
| |
| Map<String, Target> targets = getTargets(); |
| Set<String> targetNames = targets.keySet(); |
| List<ICompletionProposal> proposals = new ArrayList<>(targets.size() - 2); // current target and implicit target |
| for (String targetName : targetNames) { |
| if (targetName.equals(currentTargetName)) { |
| continue; |
| } |
| if (targetName.toLowerCase().startsWith(prefix) && targetName.length() > 0) { |
| proposals.add(new AntCompletionProposal(targetName, cursorPosition |
| - prefix.length(), prefix.length(), targetName.length(), getTargetImage(targetName), targetName, targets.get(targetName).getDescription(), AntCompletionProposal.TASK_PROPOSAL)); |
| } |
| } |
| return proposals.toArray(new ICompletionProposal[proposals.size()]); |
| } |
| |
| /** |
| * Retrieves the representative image of a target of the given name. If the target cannot be found, <code>null</code> will be returned. |
| * |
| * @param targetName |
| * the target's name |
| * @return an image suitable for representing the target, or <code>null</code> if the target cannot be found |
| * @since 3.6 |
| */ |
| private Image getTargetImage(String targetName) { |
| AntTargetNode targetNode = antModel.getTargetNode(targetName); |
| if (targetNode == null) { |
| return null; |
| } else if (targetNode.isInternal()) { |
| return AntUIImages.getImage(IAntUIConstants.IMG_ANT_TARGET_INTERNAL); |
| } else if (targetNode.isDefaultTarget()) { |
| return AntUIImages.getImage(IAntUIConstants.IMG_ANT_DEFAULT_TARGET); |
| } else { |
| return AntUIImages.getImage(IAntUIConstants.IMG_ANT_TARGET); |
| } |
| } |
| |
| private ICompletionProposal[] getDependsValueProposals(IDocument document, String prefix) { |
| List<String> possibleDependencies = new ArrayList<>(); |
| String currentTargetName = getEnclosingTargetName(document, lineNumber, columnNumber); |
| if (currentTargetName == null) { |
| return NO_PROPOSALS; |
| } |
| |
| Map<String, Target> targets = getTargets(); |
| Set<String> targetNames = targets.keySet(); |
| Enumeration<String> dependencies = null; |
| for (String targetName : targetNames) { |
| if (targetName.equals(currentTargetName)) { |
| Target currentTarget = targets.get(targetName); |
| dependencies = currentTarget.getDependencies(); |
| continue; |
| } |
| if (targetName.toLowerCase().startsWith(prefix) && targetName.length() > 0) { |
| possibleDependencies.add(targetName); |
| } |
| } |
| if (dependencies != null) { |
| while (dependencies.hasMoreElements()) { |
| possibleDependencies.remove(dependencies.nextElement()); |
| } |
| } |
| ArrayList<ICompletionProposal> proposals = new ArrayList<>(possibleDependencies.size()); |
| for (String targetName : possibleDependencies) { |
| ICompletionProposal proposal = new AntCompletionProposal(targetName, cursorPosition |
| - prefix.length(), prefix.length(), targetName.length(), getTargetImage(targetName), targetName, targets.get(targetName).getDescription(), AntCompletionProposal.TASK_PROPOSAL); |
| proposals.add(proposal); |
| } |
| return proposals.toArray(new ICompletionProposal[proposals.size()]); |
| } |
| |
| /** |
| * Returns all possible attributes for the specified task. |
| * |
| * @param taskName |
| * the name of the task for that the attribute shall be completed |
| * @param prefix |
| * prefix, that all proposals should start with. The prefix may be an empty string. |
| */ |
| protected ICompletionProposal[] getAttributeProposals(String taskName, String prefix) { |
| List<ICompletionProposal> proposals = new ArrayList<>(); |
| IElement element = getDtd().getElement(taskName); |
| if (element != null) { |
| Iterator<String> keys = element.getAttributes().keySet().iterator(); |
| while (keys.hasNext()) { |
| String attrName = keys.next(); |
| if (prefix.length() == 0 || attrName.toLowerCase().startsWith(prefix)) { |
| IAttribute dtdAttributes = element.getAttributes().get(attrName); |
| String replacementString = attrName + "=\"\""; //$NON-NLS-1$ |
| String displayString = attrName; |
| String[] items = dtdAttributes.getEnum(); |
| if (items != null) { |
| if (items.length > 1) { |
| displayString += " - ("; //$NON-NLS-1$ |
| } |
| for (int i = 0; i < items.length; i++) { |
| displayString += items[i]; |
| if (i + 1 < items.length) { |
| displayString += " | "; //$NON-NLS-1$ |
| } else { |
| displayString += ")"; //$NON-NLS-1$ |
| } |
| } |
| } |
| |
| addAttributeProposal(taskName, prefix, proposals, attrName, replacementString, displayString, true); |
| } |
| } |
| } else { // possibly a user defined task or type |
| AntTypeDefinition taskClass = getTaskClass(taskName); |
| if (taskClass != null) { |
| if (MACROINSTANCE_NAME.equals(taskClass.getClassName())) { |
| addMacroDefAttributeProposals(taskName, prefix, proposals); |
| } else { |
| IntrospectionHelper helper = getIntrospectionHelper(taskClass); |
| if (helper != null) { |
| addAttributeProposals(helper, taskName, prefix, proposals); |
| } |
| } |
| } else { // nested user defined element |
| AntTypeDefinition nestedType = getNestedType(); |
| if (nestedType != null) { |
| IntrospectionHelper helper = getIntrospectionHelper(nestedType); |
| if (helper != null) { |
| addAttributeProposals(helper, taskName, prefix, proposals); |
| } |
| } |
| } |
| } |
| return proposals.toArray(new ICompletionProposal[proposals.size()]); |
| } |
| |
| private void addAttributeProposals(IntrospectionHelper helper, String taskName, String prefix, List<ICompletionProposal> proposals) { |
| Enumeration<String> attributes = helper.getAttributes(); |
| while (attributes.hasMoreElements()) { |
| String attribute = attributes.nextElement(); |
| if (prefix.length() == 0 || attribute.toLowerCase().startsWith(prefix)) { |
| String replacementString = attribute + "=\"\""; //$NON-NLS-1$ |
| addAttributeProposal(taskName, prefix, proposals, attribute, replacementString, attribute, false); |
| } |
| } |
| } |
| |
| private AntTypeDefinition getNestedType() { |
| AntElementNode currentNode = antModel.getNode(cursorPosition, false); |
| if (currentNode == null) { |
| return null; |
| } |
| IAntElement parent = currentNode.getParentNode(); |
| if (parent instanceof AntTaskNode) { |
| String parentName = parent.getName(); |
| if (hasNestedElements(parentName)) { |
| AntTypeDefinition taskClass = getTaskClass(parentName); |
| if (taskClass != null) { |
| IntrospectionHelper helper = getIntrospectionHelper(taskClass); |
| if (helper != null) { |
| try { |
| Class<?> nestedType = helper.getElementType(currentNode.getName()); |
| // TODO probably a much better way to find the definition of a nested type than this |
| AntTypeDefinition def = new AntTypeDefinition(); |
| def.setClass(nestedType); |
| def.setClassName(nestedType.getName()); |
| return def; |
| } |
| catch (BuildException be) { |
| // do nothing |
| } |
| } |
| } |
| } |
| } |
| return null; |
| } |
| |
| private IntrospectionHelper getIntrospectionHelper(AntTypeDefinition taskClass) { |
| Project p = antModel.getProjectNode().getProject(); |
| Class<?> clazz = taskClass.getExposedClass(p); |
| if (clazz != null) { |
| IntrospectionHelper helper = null; |
| try { |
| helper = IntrospectionHelper.getHelper(antModel.getProjectNode().getProject(), clazz); |
| } |
| catch (NoClassDefFoundError e) { |
| // ignore as a task may require additional classpath components |
| } |
| return helper; |
| } |
| return null; |
| } |
| |
| private void addMacroDefAttributeProposals(String taskName, String prefix, List<ICompletionProposal> proposals) { |
| currentProposalMode = PROPOSAL_MODE_ATTRIBUTE_PROPOSAL; |
| AntDefiningTaskNode node = antModel.getDefininingTaskNode(taskName); |
| Object task = node.getRealTask(); |
| if (!(task instanceof MacroDef)) { |
| return; |
| } |
| List<Attribute> attributes = ((MacroDef) task).getAttributes(); |
| Iterator<Attribute> itr = attributes.iterator(); |
| while (itr.hasNext()) { |
| MacroDef.Attribute attribute = itr.next(); |
| String attributeName = attribute.getName(); |
| if (!(prefix.length() == 0 || attributeName.toLowerCase().startsWith(prefix))) { |
| continue; |
| } |
| String replacementString = attributeName + "=\"\""; //$NON-NLS-1$ |
| String proposalInfo = null; |
| |
| String description = attribute.getDescription(); |
| if (description != null) { |
| proposalInfo = description; |
| } |
| String deflt = attribute.getDefault(); |
| if (deflt != null && deflt.length() > 0) { |
| proposalInfo = (proposalInfo == null ? "<BR><BR>" : (proposalInfo += "<BR><BR>")); //$NON-NLS-1$ //$NON-NLS-2$ |
| proposalInfo += MessageFormat.format(AntEditorMessages.getString("AntEditorCompletionProcessor.59"), new Object[] { deflt }); //$NON-NLS-1$ |
| } |
| |
| ICompletionProposal proposal = new AntCompletionProposal(replacementString, cursorPosition |
| - prefix.length(), prefix.length(), attributeName.length() |
| + 2, null, attributeName, proposalInfo, AntCompletionProposal.TASK_PROPOSAL); |
| proposals.add(proposal); |
| } |
| } |
| |
| private void addMacroDefElementProposals(String taskName, String prefix, List<ICompletionProposal> proposals) { |
| AntDefiningTaskNode node = antModel.getDefininingTaskNode(taskName); |
| Object task = node.getRealTask(); |
| if (!(task instanceof MacroDef)) { |
| return; |
| } |
| Map<String, TemplateElement> elements = ((MacroDef) task).getElements(); |
| Iterator<String> itr = elements.keySet().iterator(); |
| int prefixLength = prefix.length(); |
| int replacementOffset = cursorPosition - prefixLength; |
| while (itr.hasNext()) { |
| String elementName = itr.next(); |
| if (!(prefixLength == 0 || elementName.toLowerCase().startsWith(prefix))) { |
| continue; |
| } |
| MacroDef.TemplateElement element = elements.get(elementName); |
| String replacementString = MessageFormat.format("<{0}>\n</{1}>", new Object[] { elementName, elementName }); //$NON-NLS-1$ |
| String proposalInfo = null; |
| |
| String description = element.getDescription(); |
| if (description != null) { |
| proposalInfo = description; |
| } |
| proposalInfo = (proposalInfo == null ? "<BR><BR>" : (proposalInfo += "<BR><BR>")); //$NON-NLS-1$ //$NON-NLS-2$ |
| |
| if (element.isOptional()) { |
| proposalInfo += AntEditorMessages.getString("AntEditorCompletionProcessor.1"); //$NON-NLS-1$ |
| } else { |
| proposalInfo += AntEditorMessages.getString("AntEditorCompletionProcessor.2"); //$NON-NLS-1$ |
| } |
| |
| ICompletionProposal proposal = new AntCompletionProposal(replacementString, replacementOffset, prefixLength, elementName.length() |
| + 2, null, elementName, proposalInfo, AntCompletionProposal.TASK_PROPOSAL); |
| proposals.add(proposal); |
| } |
| } |
| |
| private void addAttributeProposal(String taskName, String prefix, List<ICompletionProposal> proposals, String attrName, String replacementString, String displayString, boolean lookupDescription) { |
| |
| String proposalInfo = null; |
| if (lookupDescription) { |
| String required = getDescriptionProvider().getRequiredAttributeForTaskAttribute(taskName, attrName); |
| if (required != null && required.length() > 0) { |
| proposalInfo = AntEditorMessages.getString("AntEditorCompletionProcessor.Required___4") + required; //$NON-NLS-1$ |
| proposalInfo += "<BR><BR>"; //$NON-NLS-1$ |
| } |
| String description = getDescriptionProvider().getDescriptionForTaskAttribute(taskName, attrName); |
| if (description != null) { |
| proposalInfo = (proposalInfo == null ? IAntCoreConstants.EMPTY_STRING : proposalInfo); |
| proposalInfo += description; |
| } |
| } |
| |
| ICompletionProposal proposal = new AntCompletionProposal(replacementString, cursorPosition |
| - prefix.length(), prefix.length(), attrName.length() + 2, null, displayString, proposalInfo, AntCompletionProposal.TASK_PROPOSAL); |
| proposals.add(proposal); |
| } |
| |
| /** |
| * Returns all possible values for the specified attribute of the specified task. |
| * |
| * @param taskName |
| * the name of the task that the specified attribute belongs to. |
| * |
| * @param attributeName |
| * the name of the attribute for that the value shall be completed |
| * |
| * @param prefix |
| * the prefix that all proposals should start with. The prefix may be an empty string. |
| */ |
| private ICompletionProposal[] getAttributeValueProposals(String taskName, String attributeName, String prefix) { |
| List<ICompletionProposal> proposals = new ArrayList<>(); |
| IElement taskElement = getDtd().getElement(taskName); |
| if (taskElement != null) { |
| IAttribute attribute = taskElement.getAttributes().get(attributeName); |
| if (attribute != null) { |
| String[] items = attribute.getEnum(); |
| if (items != null) { |
| for (String item : items) { |
| if (prefix.length() == 0 || item.toLowerCase().startsWith(prefix)) { |
| proposals.add(new AntCompletionProposal(item, cursorPosition |
| - prefix.length(), prefix.length(), item.length(), null, item, null, AntCompletionProposal.TASK_PROPOSAL)); |
| } |
| } |
| } |
| } |
| } else { // possibly a user defined task or type |
| AntTypeDefinition taskClass = getTaskClass(taskName); |
| if (taskClass != null) { |
| IntrospectionHelper helper = getIntrospectionHelper(taskClass); |
| if (helper != null) { |
| addAttributeValueProposals(helper, attributeName, prefix, proposals); |
| } |
| } else { // nested user defined element |
| AntTypeDefinition nestedType = getNestedType(); |
| if (nestedType != null) { |
| IntrospectionHelper helper = getIntrospectionHelper(nestedType); |
| if (helper != null) { |
| addAttributeValueProposals(helper, attributeName, prefix, proposals); |
| } |
| } |
| } |
| } |
| return proposals.toArray(new ICompletionProposal[proposals.size()]); |
| } |
| |
| private void addAttributeValueProposals(IntrospectionHelper helper, String attributeName, String prefix, List<ICompletionProposal> proposals) { |
| Enumeration<String> attributes = helper.getAttributes(); |
| while (attributes.hasMoreElements()) { |
| String attribute = attributes.nextElement(); |
| if (attribute.equals(attributeName)) { |
| Class<?> attributeType = helper.getAttributeType(attribute); |
| addAttributeValueProposalsForAttributeType(attributeType, prefix, proposals); |
| break; |
| } |
| } |
| } |
| |
| private void addAttributeValueProposalsForAttributeType(Class<?> attributeType, String prefix, List<ICompletionProposal> proposals) { |
| if ((attributeType == Boolean.TYPE || attributeType == Boolean.class) && prefix.length() <= 5) { |
| addBooleanAttributeValueProposals(prefix, proposals); |
| } else if (EnumeratedAttribute.class.isAssignableFrom(attributeType)) { |
| try { |
| addEnumeratedAttributeValueProposals(attributeType, prefix, proposals); |
| } |
| catch (InstantiationException | IllegalAccessException | IllegalArgumentException | InvocationTargetException | NoSuchMethodException |
| | SecurityException e) { |
| // do nothing |
| } |
| } else if (Reference.class == attributeType) { |
| proposals.addAll(Arrays.asList(getReferencesValueProposals(prefix))); |
| } |
| } |
| |
| private void addEnumeratedAttributeValueProposals(Class<?> type, String prefix, List<ICompletionProposal> proposals) throws InstantiationException, IllegalAccessException, IllegalArgumentException, InvocationTargetException, NoSuchMethodException, SecurityException { |
| EnumeratedAttribute ea = (EnumeratedAttribute) type.getConstructor().newInstance(); |
| String enumerated; |
| for (String value : ea.getValues()) { |
| enumerated = value.toLowerCase(); |
| if (prefix.length() == 0 || enumerated.startsWith(prefix)) { |
| proposals.add(new AntCompletionProposal(enumerated, cursorPosition |
| - prefix.length(), prefix.length(), enumerated.length(), null, enumerated, null, AntCompletionProposal.TASK_PROPOSAL)); |
| } |
| } |
| } |
| |
| private void addBooleanAttributeValueProposals(String prefix, List<ICompletionProposal> proposals) { |
| String booleanAssist; |
| for (String booleanValue : new String[] { "true", "false", "on", "off", "yes", "no" }) { //$NON-NLS-1$ //$NON-NLS-2$//$NON-NLS-3$ //$NON-NLS-4$ //$NON-NLS-5$ //$NON-NLS-6$ |
| booleanAssist = booleanValue.toLowerCase(); |
| if (prefix.length() == 0 || booleanAssist.startsWith(prefix)) { |
| proposals.add(new AntCompletionProposal(booleanAssist, cursorPosition |
| - prefix.length(), prefix.length(), booleanAssist.length(), null, booleanAssist, null, AntCompletionProposal.TASK_PROPOSAL)); |
| } |
| } |
| } |
| |
| /** |
| * Returns all possible properties for the specified prefix. |
| * <P> |
| * Note that the completion mode must be property mode, otherwise it is not safe to call this method. |
| */ |
| protected ICompletionProposal[] getPropertyProposals(IDocument document, String prefix, int aCursorPosition) { |
| List<ICompletionProposal> proposals = new ArrayList<>(); |
| Map<String, ICompletionProposal> displayStringToProposals = new HashMap<>(); |
| Map<String, Object> properties = findPropertiesFromDocument(); |
| |
| Image image = AntUIImages.getImage(IAntUIConstants.IMG_PROPERTY); |
| // Determine replacement length and offset |
| // String from beginning to the beginning of the prefix |
| int replacementLength = prefix.length(); |
| int replacementOffset = 0; |
| String text = document.get(); |
| String stringToPrefix = text.substring(0, aCursorPosition - prefix.length()); |
| // Property proposal |
| String lastTwoCharacters = stringToPrefix.substring(stringToPrefix.length() - 2, stringToPrefix.length()); |
| boolean appendBraces = true; |
| if (lastTwoCharacters.equals("${")) { //$NON-NLS-1$ |
| replacementLength += 2; |
| replacementOffset = aCursorPosition - prefix.length() - 2; |
| } else if (lastTwoCharacters.endsWith("$")) { //$NON-NLS-1$ |
| replacementLength += 1; |
| replacementOffset = aCursorPosition - prefix.length() - 1; |
| } else { |
| // support for property proposals for the if/unless attributes of targets |
| replacementOffset = aCursorPosition - prefix.length(); |
| appendBraces = false; |
| } |
| |
| if (text.length() > aCursorPosition && text.charAt(aCursorPosition) == '}') { |
| replacementLength += 1; |
| } |
| String propertyName; |
| for (Iterator<String> i = properties.keySet().iterator(); i.hasNext();) { |
| propertyName = i.next(); |
| if (prefix.length() == 0 || propertyName.toLowerCase().startsWith(prefix)) { |
| String additionalPropertyInfo = (String) properties.get(propertyName); |
| |
| StringBuilder replacementString = new StringBuilder(); |
| if (appendBraces) { |
| replacementString.append("${"); //$NON-NLS-1$ |
| } |
| replacementString.append(propertyName); |
| if (appendBraces) { |
| replacementString.append('}'); |
| } |
| |
| if (displayStringToProposals.get(propertyName) == null) { |
| ICompletionProposal proposal = new AntCompletionProposal(replacementString.toString(), replacementOffset, replacementLength, replacementString.length(), image, propertyName, additionalPropertyInfo, AntCompletionProposal.PROPERTY_PROPOSAL); |
| proposals.add(proposal); |
| displayStringToProposals.put(propertyName, proposal); |
| } |
| } |
| } |
| return proposals.toArray(new ICompletionProposal[proposals.size()]); |
| } |
| |
| /** |
| * Returns all possible proposals for the specified parent name. |
| * <P> |
| * No completions will be returned if <code>parentName</code> is not known. |
| * |
| * @param document |
| * the entire document |
| * @param parentName |
| * name of the parent (surrounding) element. |
| * @param prefix |
| * the prefix that all proposals should start with. The prefix may be an empty string. |
| */ |
| protected ICompletionProposal[] getTaskProposals(IDocument document, String parentName, String prefix) { |
| List<ICompletionProposal> proposals = new ArrayList<>(250); |
| ICompletionProposal proposal; |
| if (areTasksOrTypesValidChildren(parentName)) { |
| // use the definitions in the project as that includes more than what is defined in the DTD |
| Project project = antModel.getProjectNode().getProject(); |
| Map<String, AntTypeDefinition> tasksAndTypes = ComponentHelper.getComponentHelper(project).getAntTypeTable(); |
| createProposals(document, prefix, proposals, tasksAndTypes); |
| if (parentName.equals("project")) { //$NON-NLS-1$ |
| if ("target".startsWith(prefix)) { //$NON-NLS-1$ |
| proposals.add(newCompletionProposal(document, prefix, "target")); //$NON-NLS-1$ |
| } |
| if ("extension-point".startsWith(prefix)) { //$NON-NLS-1$ |
| proposals.add(newCompletionProposal(document, prefix, "extension-point")); //$NON-NLS-1$ |
| } |
| } |
| } else { |
| IElement parent = getDtd().getElement(parentName); |
| if (parent != null) { |
| IDfm dfm = parent.getDfm(); |
| String[] accepts = dfm.getAccepts(); |
| if (accepts.length == 0) { |
| currentProposalMode = PROPOSAL_MODE_NONE; |
| } |
| String elementName; |
| for (String accept : accepts) { |
| elementName = accept; |
| if (prefix.length() == 0 || elementName.toLowerCase().startsWith(prefix)) { |
| proposal = newCompletionProposal(document, prefix, elementName); |
| proposals.add(proposal); |
| } |
| } |
| } else { |
| // a nested element of a user defined task/type? |
| AntTypeDefinition taskClass = getTaskClass(parentName); |
| if (taskClass != null) { |
| if (MACROINSTANCE_NAME.equals(taskClass.getClassName())) { |
| currentProposalMode = PROPOSAL_MODE_ATTRIBUTE_PROPOSAL; |
| addMacroDefElementProposals(parentName, prefix, proposals); |
| } else { |
| currentProposalMode = PROPOSAL_MODE_NESTED_ELEMENT_PROPOSAL; |
| IntrospectionHelper helper = getIntrospectionHelper(taskClass); |
| if (helper != null) { |
| Enumeration<String> nested = helper.getNestedElements(); |
| String nestedElement; |
| while (nested.hasMoreElements()) { |
| nestedElement = nested.nextElement(); |
| if (prefix.length() == 0 || nestedElement.toLowerCase().startsWith(prefix)) { |
| proposal = newCompletionProposal(document, prefix, nestedElement); |
| proposals.add(proposal); |
| } |
| } |
| } |
| } |
| } |
| } |
| } |
| |
| proposal = getClosingTaskProposal(getOpenElementName(), prefix, false); |
| if (proposal != null) { |
| proposals.add(proposal); |
| } |
| |
| return proposals.toArray(new ICompletionProposal[proposals.size()]); |
| } |
| |
| private boolean areTasksOrTypesValidChildren(String parentName) { |
| return parentName == "project" || parentName == "target" || parentName == "sequential" //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ |
| || parentName == "presetdef" || parentName == "parallel" || parentName == "daemons" //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ |
| || parentName == "extension-point"; //$NON-NLS-1$ |
| } |
| |
| /** |
| * Returns proposals that define the structure of a build file. |
| * |
| * Note that template proposals which define the structure of a build file are handled by {@link #determineTemplateProposals(ITextViewer, int)} |
| * which limits proposals by context type. |
| * |
| * @param document |
| * the entire document |
| * @param prefix |
| * the prefix that all proposals should start with. The prefix may be an empty string. |
| */ |
| protected ICompletionProposal[] getBuildFileProposals(IDocument document, String prefix) { |
| String rootElementName = "project"; //$NON-NLS-1$ |
| IElement rootElement = getDtd().getElement(rootElementName); |
| if (rootElement != null && rootElementName.toLowerCase().startsWith(prefix)) { |
| ICompletionProposal proposal = newCompletionProposal(document, prefix, rootElementName); |
| return new ICompletionProposal[] { proposal }; |
| } |
| |
| return NO_PROPOSALS; |
| } |
| |
| private void createProposals(IDocument document, String prefix, List<ICompletionProposal> proposals, Map<String, AntTypeDefinition> tasks) { |
| Iterator<String> keys = tasks.keySet().iterator(); |
| ICompletionProposal proposal; |
| String key; |
| while (keys.hasNext()) { |
| key = antModel.getUserNamespaceCorrectName(keys.next()); |
| if (prefix.length() == 0 || key.toLowerCase().startsWith(prefix)) { |
| proposal = newCompletionProposal(document, prefix, key); |
| proposals.add(proposal); |
| } |
| } |
| } |
| |
| private ICompletionProposal newCompletionProposal(IDocument document, String aPrefix, String elementName) { |
| additionalProposalOffset = 0; |
| Image proposalImage = AntUIImages.getImage(IAntUIConstants.IMG_TASK_PROPOSAL); |
| String proposalInfo = getDescriptionProvider().getDescriptionForTask(elementName); |
| boolean hasNestedElements = hasNestedElements(elementName); |
| String replacementString = getTaskProposalReplacementString(elementName, hasNestedElements); |
| int replacementOffset = cursorPosition - aPrefix.length(); |
| int replacementLength = aPrefix.length(); |
| if (replacementOffset > 0 && document.get().charAt(replacementOffset - 1) == '<') { |
| replacementOffset--; |
| replacementLength++; |
| } |
| int proposalCursorPosition; |
| if (hasNestedElements) { |
| proposalCursorPosition = elementName.length() + 2 + additionalProposalOffset; |
| } else { |
| if (additionalProposalOffset > 0) { |
| additionalProposalOffset += 2; // <antstructure output="|"/> |
| } else { |
| additionalProposalOffset += 1; // <arg|/> |
| } |
| proposalCursorPosition = elementName.length() + additionalProposalOffset; |
| } |
| return new AntCompletionProposal(replacementString, replacementOffset, replacementLength, proposalCursorPosition, proposalImage, elementName, proposalInfo, AntCompletionProposal.TASK_PROPOSAL); |
| } |
| |
| /** |
| * Returns the one possible completion for the specified unclosed task . |
| * |
| * @param openElementName |
| * the task that hasn't been closed last |
| * @param prefix |
| * The prefix that the one possible proposal should start with. The prefix may be an empty string. |
| * @return the proposal or <code>null</code> if no closing proposal available |
| */ |
| private ICompletionProposal getClosingTaskProposal(String openElementName, String prefix, boolean closingMode) { |
| char previousChar = getPreviousChar(); |
| ICompletionProposal proposal = null; |
| if (openElementName != null) { |
| if (prefix.length() == 0 || openElementName.toLowerCase().startsWith(prefix)) { |
| StringBuilder replaceString = new StringBuilder(); |
| if (!closingMode) { |
| if (previousChar != '/') { |
| if (previousChar != '<') { |
| replaceString.append('<'); |
| } |
| replaceString.append('/'); |
| } |
| } |
| replaceString.append(openElementName); |
| replaceString.append('>'); |
| StringBuilder displayString = new StringBuilder("</"); //$NON-NLS-1$ |
| displayString.append(openElementName); |
| displayString.append('>'); |
| proposal = new AntCompletionProposal(replaceString.toString(), cursorPosition |
| - prefix.length(), prefix.length(), replaceString.length(), null, displayString.toString(), AntEditorMessages.getString("AntEditorCompletionProcessor.39"), AntCompletionProposal.TAG_CLOSING_PROPOSAL); //$NON-NLS-1$ |
| } |
| } |
| |
| return proposal; |
| } |
| |
| protected char getPreviousChar() { |
| ITextSelection selection = (ITextSelection) viewer.getSelectionProvider().getSelection(); |
| int offset = selection.getOffset(); |
| char previousChar = '?'; |
| try { |
| previousChar = viewer.getDocument().getChar(offset - 1); |
| } |
| catch (BadLocationException e) { |
| // do nothing |
| } |
| return previousChar; |
| } |
| |
| /** |
| * Returns the replacement string for the specified task name. |
| */ |
| private String getTaskProposalReplacementString(String aTaskName, boolean hasNested) { |
| StringBuffer replacement = new StringBuffer("<"); //$NON-NLS-1$ |
| replacement.append(aTaskName); |
| ProposalNode task = getDescriptionProvider().getTaskNode(aTaskName); |
| if (task != null) { |
| appendRequiredAttributes(replacement, task); |
| } |
| if (hasNested) { |
| replacement.append("></"); //$NON-NLS-1$ |
| replacement.append(aTaskName); |
| replacement.append('>'); |
| } else { |
| replacement.append("/>"); //$NON-NLS-1$ |
| } |
| return replacement.toString(); |
| } |
| |
| private void appendRequiredAttributes(StringBuffer replacement, ProposalNode task) { |
| if (task.nodes != null) { |
| boolean requiredAdded = false; |
| Entry<String, ProposalNode> entry = null; |
| for (Iterator<Entry<String, ProposalNode>> i = task.nodes.entrySet().iterator(); i.hasNext();) { |
| entry = i.next(); |
| String name = entry.getKey(); |
| ProposalNode att = entry.getValue(); |
| if ("yes".equalsIgnoreCase(att.required)) { //$NON-NLS-1$ |
| replacement.append(' '); |
| replacement.append(name); |
| replacement.append("=\"\""); //$NON-NLS-1$ |
| if (!requiredAdded) { |
| additionalProposalOffset = name.length() + 2; |
| requiredAdded = true; |
| } |
| } |
| } |
| } |
| } |
| |
| /** |
| * Returns whether the named element supports nested elements. |
| */ |
| private boolean hasNestedElements(String elementName) { |
| IElement element = getDtd().getElement(elementName); |
| if (element != null) { |
| return !element.isEmpty(); |
| } |
| AntTypeDefinition taskClass = getTaskClass(elementName); |
| if (taskClass != null) { |
| IntrospectionHelper helper = getIntrospectionHelper(taskClass); |
| if (helper != null) { |
| Enumeration<String> nested = helper.getNestedElements(); |
| return nested.hasMoreElements(); |
| } |
| } |
| return false; |
| } |
| |
| /** |
| * Finds a direct child element with <code>aChildElementName</code> of <code>anElement</code>. |
| * <P> |
| * The child will not be searched for in the whole hierarchy but only in the hierarchy step below. |
| * |
| * @return the found child or <code>null</code> if not found. |
| */ |
| protected Element findChildElementNamedOf(Element anElement, String aChildElementName) { |
| NodeList nodeList = anElement.getChildNodes(); |
| for (int i = 0; i < nodeList.getLength(); i++) { |
| Node childNode = nodeList.item(i); |
| if (childNode.getNodeType() == Node.ELEMENT_NODE) { |
| if (childNode.getNodeName().equals(aChildElementName)) { |
| return (Element) childNode; |
| } |
| } |
| } |
| return null; |
| } |
| |
| /** |
| * Determines the current prefix that should be used for completion. |
| */ |
| private String getCurrentPrefix() { |
| if (currentPrefix != null) { |
| return currentPrefix; |
| } |
| ITextSelection selection = (ITextSelection) viewer.getSelectionProvider().getSelection(); |
| IDocument doc = viewer.getDocument(); |
| return getPrefixFromDocument(doc.get(), selection.getOffset() + selection.getLength()).toLowerCase(); |
| } |
| |
| /** |
| * Returns the prefix in the specified document text with respect to the specified offset. |
| * |
| * @param aDocumentText |
| * the whole content of the edited file as String |
| * @param anOffset |
| * the cursor position |
| */ |
| protected String getPrefixFromDocument(String aDocumentText, int anOffset) { |
| if (currentPrefix != null) { |
| return currentPrefix; |
| } |
| int startOfWordToken = anOffset; |
| |
| char token = 'a'; |
| if (startOfWordToken > 0) { |
| token = aDocumentText.charAt(startOfWordToken - 1); |
| } |
| |
| while (startOfWordToken > 0 && (Character.isJavaIdentifierPart(token) || '.' == token || '-' == token || ';' == token) && !('$' == token)) { |
| startOfWordToken--; |
| if (startOfWordToken == 0) { |
| break; // word goes right to the beginning of the doc |
| } |
| token = aDocumentText.charAt(startOfWordToken - 1); |
| } |
| |
| if (startOfWordToken != anOffset) { |
| currentPrefix = aDocumentText.substring(startOfWordToken, anOffset).toLowerCase(); |
| } else { |
| currentPrefix = IAntCoreConstants.EMPTY_STRING; |
| } |
| return currentPrefix; |
| } |
| |
| /** |
| * Returns the current proposal mode. |
| */ |
| protected int determineProposalMode(IDocument document, int aCursorPosition, String aPrefix) { |
| if (currentProposalMode != -1) { |
| return currentProposalMode; |
| } |
| if (document.getLength() == 0 || (document.getLength() == 1 && document.get().equals("<"))) { //$NON-NLS-1$ |
| return PROPOSAL_MODE_BUILDFILE; |
| } |
| |
| // String from beginning of document to the beginning of the prefix |
| String text = document.get(); |
| String stringToPrefix = text.substring(0, aCursorPosition - aPrefix.length()); |
| if (stringToPrefix.length() == 0) { |
| return PROPOSAL_MODE_BUILDFILE; |
| } |
| // Is trimmable from behind |
| String trimmedString = stringToPrefix.trim(); |
| if (antModel != null && antModel.getProjectNode() == null) { |
| currentTaskString = getTaskStringFromDocumentStringToPrefix(trimmedString); |
| if ("project".equals(currentTaskString)) { //$NON-NLS-1$ |
| return PROPOSAL_MODE_ATTRIBUTE_PROPOSAL; |
| } |
| return PROPOSAL_MODE_BUILDFILE; |
| } |
| |
| char lastChar = 0; |
| if (trimmedString.length() > 0) { |
| lastChar = trimmedString.charAt(trimmedString.length() - 1); |
| } else { |
| return PROPOSAL_MODE_TASK_PROPOSAL; |
| } |
| if (stringToPrefix.charAt(stringToPrefix.length() - 1) != lastChar && lastChar != '>' && lastChar != ',') { |
| /* |
| * Substring must be trimmable from behind in case of attribute proposal because a space or a new line must be used as delimiter between |
| * task name and attribute or attribute and attribute. Example: '<property id="bla" name="hups"' |
| */ |
| |
| // Attribute proposal |
| if (lastChar != '>' && lastChar != '<') { |
| currentTaskString = getTaskStringFromDocumentStringToPrefix(trimmedString); |
| if (currentTaskString != null && isKnownElement(currentTaskString)) { |
| return PROPOSAL_MODE_ATTRIBUTE_PROPOSAL; |
| } |
| } |
| } else if (stringToPrefix.charAt(stringToPrefix.length() - 1) == '"' || trimmedString.charAt(trimmedString.length() - 1) == ',') { |
| // Attribute value proposal |
| currentTaskString = getTaskStringFromDocumentStringToPrefix(trimmedString); |
| if (currentTaskString != null && isKnownElement(currentTaskString)) { |
| return PROPOSAL_MODE_ATTRIBUTE_VALUE_PROPOSAL; |
| } |
| } else { // Possibly a Task proposal |
| int spaceIndex = stringToPrefix.lastIndexOf(' '); |
| int lessThanIndex = stringToPrefix.lastIndexOf('<'); |
| int greaterThanIndex = stringToPrefix.lastIndexOf('>'); |
| // Task proposal |
| if (greaterThanIndex < lessThanIndex) { |
| // we are inside an open element |
| if (lastChar == '$') { |
| return PROPOSAL_MODE_PROPERTY_PROPOSAL; |
| } |
| if (lessThanIndex > spaceIndex) { |
| int slashIndex = stringToPrefix.lastIndexOf('/'); |
| if (slashIndex == lessThanIndex + 1) { |
| return PROPOSAL_MODE_TASK_PROPOSAL_CLOSING; // ... </ |
| } |
| return PROPOSAL_MODE_TASK_PROPOSAL; |
| } |
| } |
| if (lessThanIndex < greaterThanIndex) { |
| if (isPropertyProposalMode(stringToPrefix)) { |
| return PROPOSAL_MODE_PROPERTY_PROPOSAL; |
| } |
| return PROPOSAL_MODE_TASK_PROPOSAL; |
| } |
| } |
| |
| // Property proposal |
| if (isPropertyProposalMode(stringToPrefix)) { |
| return PROPOSAL_MODE_PROPERTY_PROPOSAL; |
| } |
| |
| return PROPOSAL_MODE_NONE; |
| } |
| |
| private boolean isPropertyProposalMode(String stringToPrefix) { |
| if (stringToPrefix.length() >= 2) { |
| String lastTwoChars = stringToPrefix.substring(stringToPrefix.length() - 2, stringToPrefix.length()); |
| if (lastTwoChars.equals("${") || //$NON-NLS-1$ |
| stringToPrefix.charAt(stringToPrefix.length() - 1) == '$') { |
| return true; |
| } |
| } |
| return false; |
| } |
| |
| /** |
| * Returns the last occurring task string in the specified string. |
| * <P> |
| * The returned string must not necessarily be a valid Ant task string. This can be tested with the method <code>inNamedTaskKnown(String)</code> |
| * after invoking this method. |
| * |
| * @param aDocumentStringToPrefix |
| * the String that contains the whole string of the currently edited file from the beginning up to the prefix for code completion. |
| * Example: {@literal '<project default="name"><property '}. |
| * |
| * @return the extracted task string or <code>null</code> if no string could be extracted. |
| */ |
| private String getTaskStringFromDocumentStringToPrefix(String aDocumentStringToPrefix) { |
| |
| int lessThanIndex = aDocumentStringToPrefix.lastIndexOf('<'); |
| |
| if (lessThanIndex > -1) { |
| String taskString = aDocumentStringToPrefix.trim(); |
| taskString = taskString.substring(lessThanIndex + 1, taskString.length()); |
| int index = taskString.indexOf(' '); |
| if (index > 0) { |
| taskString = taskString.substring(0, index); |
| } |
| index = taskString.indexOf('\n'); |
| if (index > 0) { |
| taskString = taskString.substring(0, index); |
| } |
| index = taskString.indexOf('\r'); |
| if (index > 0) { |
| taskString = taskString.substring(0, index); |
| } |
| return taskString; |
| } |
| |
| return null; |
| } |
| |
| /** |
| * Returns the last occurring attribute string in the specified string. <code>null</code> is returned if no attribute string is available. |
| * <P> |
| * Calling this method is only safe if the current proposal mode is really <code>PROPOSAL_MODE_ATTRIBUTE_VALUE_PROPOSAL</code>. |
| */ |
| public static String getAttributeStringFromDocumentStringToPrefix(String docStringToPrefix) { |
| int index = docStringToPrefix.lastIndexOf('='); |
| if (index == -1) { |
| return null; |
| } |
| String subString = docStringToPrefix.substring(0, index); |
| subString = subString.trim(); |
| |
| index = subString.lastIndexOf(' '); |
| if (index > 0) { |
| subString = subString.substring(index + 1, subString.length()); |
| } |
| index = subString.lastIndexOf('\n'); |
| if (index > 0) { |
| subString = subString.substring(index + 1, subString.length()); |
| } |
| index = subString.lastIndexOf('\r'); |
| if (index > 0) { |
| subString = subString.substring(index + 1, subString.length()); |
| } |
| return trimBeginning(subString); |
| } |
| |
| private static String trimBeginning(String toBeTrimmed) { |
| int i = 0; |
| while ((i != toBeTrimmed.length()) && Character.isWhitespace(toBeTrimmed.charAt(i))) { |
| i++; |
| } |
| return toBeTrimmed.substring(i); |
| } |
| |
| /** |
| * Returns whether the specified element name is known |
| */ |
| protected boolean isKnownElement(String elementName) { |
| if (elementName.equals("target") || elementName.equals("project") || elementName.equals("extension-point")) { //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ |
| return true; |
| } |
| AntProjectNode node = antModel.getProjectNode(); |
| if (node != null) { |
| Project antProject = node.getProject(); |
| ComponentHelper helper = ComponentHelper.getComponentHelper(antProject); |
| if (helper.getDefinition(elementName) != null) { |
| return true; |
| } |
| if (helper.getDefinition(antModel.getNamespaceCorrectName(elementName)) != null) { |
| return true; |
| } |
| // not everything is a task or type (nested elements) |
| if (getDtd().getElement(elementName) != null) { |
| return true; |
| } |
| |
| if (getNestedType() != null) { |
| return true; |
| } |
| } |
| |
| return false; |
| } |
| |
| private AntTypeDefinition getTaskClass(String taskName) { |
| AntTypeDefinition clss = null; |
| AntProjectNode node = antModel.getProjectNode(); |
| if (node != null) { |
| Project antProject = node.getProject(); |
| Map<String, AntTypeDefinition> tasksAndTypes = ComponentHelper.getComponentHelper(antProject).getAntTypeTable(); |
| clss = tasksAndTypes.get(taskName); |
| if (clss == null) { |
| clss = tasksAndTypes.get(antModel.getNamespaceCorrectName(taskName)); |
| } |
| } |
| return clss; |
| } |
| |
| /** |
| * Finds the parent task element in respect to the cursor position. |
| * |
| * @return the parent task element or <code>null</code> if not found. |
| */ |
| protected String getParentName(IDocument document, int aLineNumber, int aColumnNumber) { |
| if (document.getLength() == 0) { |
| return null; |
| } |
| AntProjectNode project = antModel.getProjectNode(); |
| if (project == null) { |
| return null; |
| } |
| int offset = getOffset(document, aLineNumber, aColumnNumber); |
| if (offset == -1) { |
| return null; |
| } |
| IAntElement node = project.getNode(offset); |
| if (node == null) { |
| node = antModel.getOpenElement(); |
| } |
| if (node == null) { |
| return IAntCoreConstants.EMPTY_STRING; |
| } else if (node instanceof AntTaskNode) { |
| String name = node.getName(); |
| if (offset <= node.getOffset() + name.length() - 1) { |
| // not really the enclosing node as the offset is within the name of the node |
| node = node.getParentNode(); |
| } else { |
| return name; |
| } |
| } |
| if (node instanceof AntTaskNode) { |
| return node.getName(); |
| } else if (node instanceof AntTargetNode) { |
| return "target"; //$NON-NLS-1$ |
| } else { |
| return "project"; //$NON-NLS-1$ |
| } |
| } |
| |
| /** |
| * Return the properties as defined in the entire buildfile |
| * |
| * @return a map with all the found properties |
| */ |
| private Map<String, Object> findPropertiesFromDocument() { |
| Project project = antModel.getProjectNode().getProject(); |
| return project.getProperties(); |
| } |
| |
| private Map<String, Target> getTargets() { |
| Project project = antModel.getProjectNode().getProject(); |
| return project.getTargets(); |
| } |
| |
| protected File getEditedFile() { |
| IWorkbenchPage page = AntUIPlugin.getActivePage(); |
| if (page == null) { |
| return null; |
| } |
| IEditorPart editor = page.getActiveEditor(); |
| if (editor == null) { |
| return null; |
| } |
| FileEditorInput input = (FileEditorInput) editor.getEditorInput(); |
| String projectPath = input.getFile().getProject().getLocation().toFile().getAbsolutePath(); |
| String projectRelativeFilePath = input.getFile().getFullPath().removeFirstSegments(1).makeRelative().toString(); |
| return new File(projectPath + File.separator + projectRelativeFilePath); |
| } |
| |
| private String getOpenElementName() { |
| AntElementNode node = antModel.getOpenElement(); |
| if (node == null) { |
| return null; |
| } |
| return node.getName(); |
| } |
| |
| /** |
| * Finds the enclosing target in respect to the cursor position and returns its name |
| * |
| * @return the name of the enclosing target or <code>null</code> if not found or the element is not contained in a target. |
| */ |
| private String getEnclosingTargetName(IDocument document, int aLineNumber, int aColumnNumber) { |
| |
| AntProjectNode project = antModel.getProjectNode(); |
| int offset = getOffset(document, aLineNumber, aColumnNumber); |
| if (offset == -1) { |
| return null; |
| } |
| IAntElement node = project.getNode(offset); |
| if (node instanceof AntTaskNode) { |
| node = node.getParentNode(); |
| if (!(node instanceof AntTargetNode)) { |
| // top level task |
| node = null; |
| } |
| } else if (node instanceof AntProjectNode) { |
| node = null; |
| } |
| String targetName = null; |
| if (node == null || (targetName = ((AntTargetNode) node).getTarget().getName()) == null || targetName.length() == 0) { |
| return null; |
| } |
| return targetName; |
| } |
| |
| private int getOffset(IDocument document, int line, int column) { |
| try { |
| return document.getLineOffset(line) + column - 1; |
| } |
| catch (BadLocationException e) { |
| return -1; |
| } |
| } |
| |
| /** |
| * Sets this processor's set of characters triggering the activation of the completion proposal computation. |
| * |
| * @param activationSet |
| * the activation set |
| */ |
| public void setCompletionProposalAutoActivationCharacters(char[] activationSet) { |
| autoActivationChars = activationSet; |
| } |
| |
| @Override |
| protected String extractPrefix(ITextViewer textViewer, int offset) { |
| return getPrefixFromDocument(textViewer.getDocument().get(), offset); |
| } |
| |
| /** |
| * Cut out angular brackets for relevance sorting, since the template name does not contain the brackets. |
| */ |
| @Override |
| protected int getRelevance(Template template, String prefix) { |
| String newprefix = prefix; |
| if (newprefix.startsWith("<")) {//$NON-NLS-1$ |
| newprefix = prefix.substring(1); |
| } |
| if (template.getName().startsWith(newprefix)) { |
| return 90; |
| } |
| return 0; |
| } |
| |
| @Override |
| protected Template[] getTemplates(String contextTypeId) { |
| return AntTemplateAccess.getDefault().getTemplateStore().getTemplates(contextTypeId); |
| } |
| |
| @Override |
| protected TemplateContextType getContextType(ITextViewer textViewer, IRegion region) { |
| switch (determineProposalMode(textViewer.getDocument(), cursorPosition, getCurrentPrefix())) { |
| case PROPOSAL_MODE_TASK_PROPOSAL: |
| if (getEnclosingTargetName(textViewer.getDocument(), lineNumber, columnNumber) == null) { |
| return AntTemplateAccess.getDefault().getContextTypeRegistry().getContextType(TargetContextType.TARGET_CONTEXT_TYPE); |
| } |
| return AntTemplateAccess.getDefault().getContextTypeRegistry().getContextType(TaskContextType.TASK_CONTEXT_TYPE); |
| case PROPOSAL_MODE_BUILDFILE: |
| return AntTemplateAccess.getDefault().getContextTypeRegistry().getContextType(BuildFileContextType.BUILDFILE_CONTEXT_TYPE); |
| case PROPOSAL_MODE_NONE: |
| case PROPOSAL_MODE_ATTRIBUTE_PROPOSAL: |
| case PROPOSAL_MODE_TASK_PROPOSAL_CLOSING: |
| case PROPOSAL_MODE_ATTRIBUTE_VALUE_PROPOSAL: |
| case PROPOSAL_MODE_PROPERTY_PROPOSAL: |
| case PROPOSAL_MODE_NESTED_ELEMENT_PROPOSAL: |
| default: |
| return null; |
| } |
| } |
| |
| @Override |
| protected Image getImage(Template template) { |
| return AntUIImages.getImage(IAntUIConstants.IMG_TEMPLATE_PROPOSAL); |
| } |
| |
| @Override |
| protected TemplateContext createContext(ITextViewer contextViewer, IRegion region) { |
| TemplateContextType contextType = getContextType(contextViewer, region); |
| if (contextType != null) { |
| Point selection = contextViewer.getSelectedRange(); |
| Position position; |
| if (selection.y > 0) { |
| position = new Position(selection.x, selection.y); |
| } else { |
| position = new Position(region.getOffset(), region.getLength()); |
| } |
| |
| IDocument document = contextViewer.getDocument(); |
| return new AntContext(contextType, document, antModel, position); |
| } |
| return null; |
| } |
| |
| @Override |
| protected ICompletionProposal createProposal(Template template, TemplateContext context, IRegion region, int relevance) { |
| AntTemplateProposal proposal = new AntTemplateProposal(template, context, region, getImage(template), relevance); |
| proposal.setInformationControlCreator(new AntTemplateInformationControlCreator()); |
| return proposal; |
| } |
| |
| protected ISchema getDtd() { |
| if (fgDtd == null) { |
| IRunnableWithProgress runnable = monitor -> { |
| try { |
| fgDtd = parseDtd(); |
| } |
| catch (IOException e1) { |
| AntUIPlugin.log(e1); |
| } |
| catch (ParseError e2) { |
| AntUIPlugin.log(e2); |
| } |
| }; |
| |
| IProgressService service = PlatformUI.getWorkbench().getProgressService(); |
| try { |
| service.busyCursorWhile(runnable); |
| } |
| catch (InvocationTargetException e) { |
| // do nothing |
| } |
| catch (InterruptedException e) { |
| // do nothing |
| } |
| } |
| return fgDtd; |
| } |
| |
| /** |
| * The provider for all task and attribute descriptions. |
| */ |
| private TaskDescriptionProvider getDescriptionProvider() { |
| return TaskDescriptionProvider.getDefault(); |
| } |
| |
| protected static void resetCodeCompletionDataStructures() { |
| fgDtd = null; |
| TaskDescriptionProvider.reset(); |
| } |
| |
| @Override |
| public void assistSessionStarted(ContentAssistEvent event) { |
| IContentAssistant assistant = event.assistant; |
| if (assistant instanceof IContentAssistantExtension2) { |
| fContentAssistant = (IContentAssistantExtension2) assistant; |
| } |
| } |
| |
| @Override |
| public void assistSessionEnded(ContentAssistEvent event) { |
| fContentAssistant = null; |
| fTemplatesOnly = false; |
| } |
| |
| @Override |
| public void selectionChanged(ICompletionProposal proposal, boolean smartToggle) { |
| // do nothing |
| } |
| |
| private String getIterationGestureMessage(String showMessage) { |
| final IBindingService bindingSvc = PlatformUI.getWorkbench().getAdapter(IBindingService.class); |
| TriggerSequence[] triggers = bindingSvc.getActiveBindingsFor(getContentAssistCommand()); |
| String message; |
| if (triggers.length > 0) { |
| message = MessageFormat.format(AntEditorMessages.getString("AntEditorCompletionProcessor.63"), new Object[] { triggers[0].format(), //$NON-NLS-1$ |
| showMessage }); |
| } else { |
| message = MessageFormat.format(AntEditorMessages.getString("AntEditorCompletionProcessor.64"), new Object[] { showMessage }); //$NON-NLS-1$ |
| } |
| return message; |
| } |
| |
| private ParameterizedCommand getContentAssistCommand() { |
| final ICommandService commandSvc = PlatformUI.getWorkbench().getAdapter(ICommandService.class); |
| final Command command = commandSvc.getCommand(ITextEditorActionDefinitionIds.CONTENT_ASSIST_PROPOSALS); |
| ParameterizedCommand pCmd = new ParameterizedCommand(command, null); |
| return pCmd; |
| } |
| } |