/******************************************************************************* * Copyright (c) 2015 Red Hat, Inc. * Distributed under license by Red Hat, Inc. All rights reserved. * This program is 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: * Red Hat, Inc. - initial API and implementation ******************************************************************************/ package org.jboss.tools.batch.ui.internal.contentassist; import java.util.ArrayList; import java.util.Collection; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; import org.eclipse.core.resources.IFile; import org.eclipse.core.resources.IProject; import org.eclipse.core.runtime.IProgressMonitor; import org.eclipse.jdt.core.IJavaElement; import org.eclipse.jdt.core.IJavaProject; import org.eclipse.jdt.core.JavaCore; import org.eclipse.jdt.core.JavaModelException; import org.eclipse.jdt.core.search.IJavaSearchConstants; import org.eclipse.jdt.core.search.IJavaSearchScope; import org.eclipse.jdt.core.search.SearchEngine; import org.eclipse.jdt.core.search.SearchPattern; import org.eclipse.jdt.core.search.TypeNameRequestor; import org.eclipse.jdt.internal.ui.JavaPluginImages; import org.eclipse.wst.sse.ui.contentassist.CompletionProposalInvocationContext; import org.eclipse.wst.sse.ui.internal.contentassist.CustomCompletionProposal; import org.eclipse.wst.xml.core.internal.contentmodel.CMNode; import org.eclipse.wst.xml.core.internal.provisional.document.IDOMNode; import org.eclipse.wst.xml.ui.internal.contentassist.AbstractXMLModelQueryCompletionProposalComputer; import org.eclipse.wst.xml.ui.internal.contentassist.ContentAssistRequest; import org.eclipse.wst.xml.ui.internal.contentassist.XMLContentModelGenerator; import org.jboss.tools.batch.core.BatchArtifactType; import org.jboss.tools.batch.core.BatchConstants; import org.jboss.tools.batch.core.BatchCorePlugin; import org.jboss.tools.batch.core.IBatchArtifact; import org.jboss.tools.batch.core.IBatchProject; import org.jboss.tools.batch.core.IBatchProperty; import org.jboss.tools.batch.ui.BatchUIPlugin; import org.jboss.tools.batch.ui.JobImages; import org.jboss.tools.common.text.TextProposal; import org.jboss.tools.common.ui.CommonUIImages; import org.jboss.tools.common.xml.XMLUtilities; import org.jboss.tools.jst.web.kb.PageContextFactory; import org.w3c.dom.Attr; import org.w3c.dom.Element; import org.w3c.dom.NamedNodeMap; import org.w3c.dom.Node; import org.w3c.dom.NodeList; /** * * @author Viacheslav Kabanovich * */ @SuppressWarnings("restriction") public class BatchJobCompletionProposalComputer extends AbstractXMLModelQueryCompletionProposalComputer implements BatchConstants { CompletionProposalInvocationContext context; IFile file; IBatchProject bp; IProgressMonitor monitor; @SuppressWarnings("rawtypes") @Override public List computeCompletionProposals(CompletionProposalInvocationContext context, IProgressMonitor monitor) { this.monitor = monitor; if(context.getDocument() != null && context.getDocument().get().indexOf("<job ") < 0) { return new ArrayList(); } return super.computeCompletionProposals(context, monitor); } @Override protected XMLContentModelGenerator getContentGenerator() { return new XMLContentModelGenerator(); } @Override protected boolean validModelQueryNode(CMNode node) { return false; } @Override protected void addAttributeValueProposals(ContentAssistRequest contentAssistRequest, CompletionProposalInvocationContext context) { this.context = context; file = PageContextFactory.getResource(context.getDocument()); bp = BatchCorePlugin.getBatchProject(file.getProject(), true); if(bp == null) { return; } IDOMNode node = (IDOMNode) contentAssistRequest.getNode(); if(!(node instanceof Element)) { return; } Element current = (Element)node; int begin = contentAssistRequest.getReplacementBeginPosition(); int length = contentAssistRequest.getReplacementLength(); String matchString = contentAssistRequest.getMatchString(); if(matchString.startsWith("\"")) { matchString = matchString.substring(1); begin++; length -= 2; } Node n = findNodeForOffset(node, context.getInvocationOffset()); if(n instanceof Attr) { if(ATTR_REF.equals(n.getNodeName())) { addRefAttributeValueProposals(contentAssistRequest, context, current, matchString, begin, length); } else if(ATTR_NEXT.equals(n.getNodeName())) { addTransitionAttributeValueProposals(contentAssistRequest, context, current, matchString, begin, length, false); } else if(ATTR_TO.equals(n.getNodeName())) { current = (Element)current.getParentNode(); if(current != null) { addTransitionAttributeValueProposals(contentAssistRequest, context, current, matchString, begin, length, false); } } else if(ATTR_RESTART.equals(n.getNodeName())) { while(current != null && current.getParentNode() != null && !current.getParentNode().getNodeName().equals(TAG_JOB)) { current = (Element)current.getParentNode(); } if(current != null && current.getParentNode() != null && current.getParentNode().getNodeName().equals(TAG_JOB)) { addTransitionAttributeValueProposals(contentAssistRequest, context, current, matchString, begin, length, true); } } else if(ATTR_NAME.equals(n.getNodeName()) && TAG_PROPERTY.equals(current.getNodeName())) { addPropertyNameValueProposals(contentAssistRequest, context, current, matchString, begin, length); } else if(ATTR_CLASS.equals(n.getNodeName())) { try { addClassValueProposals(contentAssistRequest, context, current, matchString, begin, length); } catch (JavaModelException e) { BatchUIPlugin.getDefault().logError(e); } } } } private void addRefAttributeValueProposals(ContentAssistRequest contentAssistRequest, CompletionProposalInvocationContext context, Element current, String matchString, int begin, int length) { String tag = current.getNodeName(); Collection<IBatchArtifact> as = null; if(TAG_LISTENER.equals(tag)) { if(current.getParentNode() == null || !(current.getParentNode().getParentNode() instanceof Element)) { return; } Element p = (Element)current.getParentNode().getParentNode(); if(p != null && p.getNodeName().equals(TAG_JOB)) { as = bp.getArtifacts(BatchArtifactType.JOB_LISTENER); } else { as = bp.getArtifacts(BatchArtifactType.STEP_LISTENER); Element chunk = XMLUtilities.getUniqueChild(p, TAG_CHUNK); if(chunk != null) { as = new HashSet<IBatchArtifact>(); for (IBatchArtifact a: bp.getAllArtifacts()) { BatchArtifactType t = a.getArtifactType(); if(TAG_STEP.equals(t.getTag())) { as.add(a); } } } } } else { for (BatchArtifactType t: BatchArtifactType.values()) { if(t.getTag().equals(tag)) { as = bp.getArtifacts(t); } } } if(as != null) { for (IBatchArtifact a: as) { String value = a.getName(); if(!value.startsWith(matchString)) { continue; } CustomCompletionProposal proposal = new CustomCompletionProposal( value, begin, length, value.length(), JobImages.getImageByElement(a.getArtifactType()), value, null, value + ": " + a.getType().getElementName(), TextProposal.R_XML_ATTRIBUTE_VALUE); contentAssistRequest.addProposal(proposal); } } } private void addTransitionAttributeValueProposals(ContentAssistRequest contentAssistRequest, CompletionProposalInvocationContext context, Element current, String matchString, int begin, int length, boolean currentAllowed) { String currentId = current.getAttribute(ATTR_ID); Element parent = (Element)current.getParentNode(); Map<String, String> ids = new HashMap<String, String>(); NodeList nl = parent.getChildNodes(); for (int i = 0; i < nl.getLength(); i++) { Node cn = nl.item(i); if(cn instanceof Element) { String id1 = ((Element)cn).getAttribute(ATTR_ID); if(id1 != null && id1.length() > 0 && (currentAllowed || !id1.equals(currentId))) { ids.put(id1, cn.getNodeName()); } } } for (String s: ids.keySet()) { if(!s.startsWith(matchString)) { continue; } CustomCompletionProposal proposal = new CustomCompletionProposal( s, begin, length, s.length(), JobImages.getImage(JobImages.FLOW_IMAGE), s, null, "<" + ids.get(s) + " id=\"" + s + "\"" + ">", TextProposal.R_XML_ATTRIBUTE_VALUE); contentAssistRequest.addProposal(proposal); } } private void addPropertyNameValueProposals(ContentAssistRequest contentAssistRequest, CompletionProposalInvocationContext context, Element current, String matchString, int begin, int length) { Element properties = (Element)current.getParentNode(); if(properties != null && TAG_PROPERTIES.equals(properties.getNodeName())) { Set<String> existing = new HashSet<String>(); for (Element e: XMLUtilities.getChildren(properties, TAG_PROPERTY)) { if(e != current) existing.add(e.getAttribute(ATTR_NAME)); } Set<String> variants = new HashSet<String>(); Element artifactElement = (Element)properties.getParentNode(); String ref = artifactElement.getAttribute(ATTR_REF).trim(); if(ref != null && ref.length() > 0) { Collection<IBatchArtifact> a = bp.getArtifacts(ref); if(!a.isEmpty()) { for (IBatchProperty prop: a.iterator().next().getProperties()) { String nm = prop.getPropertyName(); if(!existing.contains(nm)) { variants.add(nm); } } } } while(artifactElement.getParentNode() instanceof Element) { artifactElement = (Element)artifactElement.getParentNode(); properties = XMLUtilities.getUniqueChild(artifactElement, TAG_PROPERTIES); if(properties != null) { for (Element prop: XMLUtilities.getChildren(properties, TAG_PROPERTY)) { String nm = prop.getAttribute(ATTR_NAME); if(!existing.contains(nm)) { variants.add(nm); } } } } for (String s: variants) { if(!s.startsWith(matchString)) { continue; } CustomCompletionProposal proposal = new CustomCompletionProposal( s, begin, length, s.length(), JobImages.getImage(JobImages.PROPERTY_IMAGE), s, null, s, TextProposal.R_XML_ATTRIBUTE_VALUE); contentAssistRequest.addProposal(proposal); } } } /* Utility functions */ Node findNodeForOffset(IDOMNode node, int offset) { if(node == null) return null; if (!node.contains(offset)) return null; if (node.hasChildNodes()) { // Try to find the node in children NodeList children = node.getChildNodes(); for (int i = 0; children != null && i < children.getLength(); i++) { IDOMNode child = (IDOMNode)children.item(i); if (child.contains(offset)) { return findNodeForOffset(child, offset); } } } // Not found in children or nave no children if (node.hasAttributes()) { // Try to find in the node attributes NamedNodeMap attributes = node.getAttributes(); for (int i = 0; attributes != null && i < attributes.getLength(); i++) { IDOMNode attr = (IDOMNode)attributes.item(i); if (attr.contains(offset)) { return attr; } } } // Return the node itself return node; } private void addClassValueProposals(final ContentAssistRequest contentAssistRequest, CompletionProposalInvocationContext context, Element current, String matchString, final int begin, final int length) throws JavaModelException { if (matchString.length()>0) { int lastDot = matchString.lastIndexOf('.'); String packageNameString = null; String classNameString = matchString; int packagePatern = SearchPattern.R_PREFIX_MATCH; if(lastDot>0) { if(matchString.length()>lastDot+1 && Character.isUpperCase(matchString.charAt(lastDot+1))) { classNameString = matchString.substring(lastDot+1); packageNameString = matchString.substring(0, lastDot); packagePatern = SearchPattern.R_EXACT_MATCH; } else { classNameString = null; packageNameString = matchString; } } char[] packageName = packageNameString!=null?packageNameString.toCharArray():null; char[] className = classNameString!=null?classNameString.toCharArray():null; IProject project = bp.getProject(); IJavaProject jp = JavaCore.create(project); IJavaSearchScope scope = SearchEngine.createJavaSearchScope(new IJavaElement[] {jp}); SearchEngine engine = new SearchEngine(); engine.searchAllTypeNames(packageName, packagePatern, className, SearchPattern.R_PREFIX_MATCH, IJavaSearchConstants.CLASS, scope, new TypeNameRequestor() { @Override public void acceptType( int modifiers, char[] packageName, char[] simpleTypeName, char[][] enclosingTypeNames, String path) { StringBuffer fullName = new StringBuffer(); fullName.append(packageName).append('.').append(simpleTypeName); String fn = fullName.toString(); CustomCompletionProposal proposal = new CustomCompletionProposal( fn, begin, length, fn.length(), CommonUIImages.getImage(JavaPluginImages.DESC_OBJS_CLASS), new String(simpleTypeName), fn, null, fn, TextProposal.R_XML_ATTRIBUTE_VALUE, true); contentAssistRequest.addProposal(proposal); } }, IJavaSearchConstants.FORCE_IMMEDIATE_SEARCH, monitor); } } }