| /******************************************************************************* |
| * Copyright (c) 2012 Oracle. All rights reserved. |
| * This program and the accompanying materials are made available under the |
| * terms of the Eclipse Public License v1.0 and Eclipse Distribution License v. 1.0 |
| * which accompanies this distribution. |
| * The Eclipse Public License is available at http://www.eclipse.org/legal/epl-v10.html |
| * and the Eclipse Distribution License is available at |
| * http://www.eclipse.org/org/documents/edl-v10.php. |
| * |
| * Contributors: |
| * Oracle - initial API and implementation |
| * |
| ******************************************************************************/ |
| package org.eclipse.jpt.jpa.ui.internal; |
| |
| /** |
| * This computer adds content assist support for a mapping file (ORM Configuration). |
| * |
| * @version 2.3 |
| * @since 2.3 |
| */ |
| |
| import java.util.ArrayList; |
| import java.util.Collection; |
| import java.util.Collections; |
| import java.util.List; |
| import org.eclipse.core.filebuffers.FileBuffers; |
| import org.eclipse.core.filebuffers.ITextFileBuffer; |
| import org.eclipse.core.filebuffers.ITextFileBufferManager; |
| import org.eclipse.core.resources.IFile; |
| import org.eclipse.core.resources.IWorkspaceRoot; |
| import org.eclipse.core.resources.ResourcesPlugin; |
| import org.eclipse.core.runtime.IProgressMonitor; |
| import org.eclipse.jface.text.contentassist.CompletionProposal; |
| import org.eclipse.jface.text.contentassist.ICompletionProposal; |
| import org.eclipse.jpt.common.utility.internal.CollectionTools; |
| import org.eclipse.jpt.common.utility.internal.StringTools; |
| import org.eclipse.jpt.jpa.core.JpaFile; |
| import org.eclipse.jpt.jpa.core.JpaStructureNode; |
| import org.eclipse.jpt.jpa.core.JpaXmlStructureNode; |
| import org.eclipse.jpt.jpa.ui.JptJpaUiPlugin; |
| import org.eclipse.wst.sse.ui.contentassist.CompletionProposalInvocationContext; |
| import org.eclipse.wst.sse.ui.internal.contentassist.CustomCompletionProposal; |
| import org.eclipse.wst.xml.core.internal.provisional.document.IDOMDocument; |
| import org.eclipse.wst.xml.core.internal.provisional.document.IDOMElement; |
| import org.eclipse.wst.xml.core.internal.provisional.document.IDOMNode; |
| import org.eclipse.wst.xml.ui.internal.XMLUIMessages; |
| import org.eclipse.wst.xml.ui.internal.contentassist.ContentAssistRequest; |
| import org.eclipse.wst.xml.ui.internal.contentassist.XMLRelevanceConstants; |
| import org.w3c.dom.Document; |
| import org.w3c.dom.Node; |
| |
| public class JpaXmlCompletionProposalComputer extends DefaultJpaXmlCompletionProposalComputer { |
| |
| public JpaXmlCompletionProposalComputer() { |
| } |
| |
| @Override |
| public List<ICompletionProposal> computeCompletionProposals( |
| CompletionProposalInvocationContext context, |
| IProgressMonitor monitor) { |
| try { |
| return super.computeCompletionProposals(context, monitor); |
| } catch (RuntimeException ex) { |
| // When we run into any unexpected exception, we will log the exception |
| // and then return an empty list to prevent code completion process from |
| // crashing. We need to determine if runtime exceptions should be |
| // expected. If so, we could remove the log(ex) in the future. |
| JptJpaUiPlugin.log(ex); |
| return Collections.emptyList(); |
| } |
| } |
| |
| @Override |
| protected void addAttributeValueProposals(ContentAssistRequest contentAssistRequest, |
| CompletionProposalInvocationContext context) { |
| |
| List<String> proposedValues = getProposedValues(context); |
| |
| if (proposedValues.size() > 0 ) { |
| String matchString = contentAssistRequest.getMatchString(); |
| if (matchString == null) { |
| matchString = ""; //$NON-NLS-1$ |
| } |
| if ((matchString.length() > 0) && (matchString.startsWith("\"") || matchString.startsWith("'"))) { //$NON-NLS-1$ //$NON-NLS-2$ |
| matchString = matchString.substring(1); |
| } |
| //create suggestions for this attribute value declaration |
| int rOffset = contentAssistRequest.getReplacementBeginPosition(); |
| int rLength = contentAssistRequest.getReplacementLength(); |
| for (String possibleValue : proposedValues) { |
| if ((matchString.length() == 0) || StringTools.stringStartsWithIgnoreCase(possibleValue, matchString)) { |
| String rString = "\"" + possibleValue + "\""; //$NON-NLS-1$ //$NON-NLS-2$ |
| CompletionProposal proposal = new CompletionProposal( |
| rString, rOffset, rLength, rString.length()); |
| contentAssistRequest.addProposal(proposal); |
| } |
| } |
| } |
| else { |
| setErrorMessage(XMLUIMessages.Content_Assist_not_availab_UI_); |
| } |
| } |
| |
| @SuppressWarnings("deprecation") |
| @Override |
| protected void addTagInsertionProposals( |
| ContentAssistRequest contentAssistRequest, int childPosition, |
| CompletionProposalInvocationContext context) { |
| |
| Node parent = contentAssistRequest.getParent(); |
| |
| // This is only valid at the document element level |
| // and only valid if it's a XML file |
| if ((parent != null) && (parent.getNodeType() == Node.DOCUMENT_NODE) && |
| ((IDOMDocument) parent).isXMLType() && !isCursorAfterXMLPI(contentAssistRequest)) { |
| return; |
| } |
| // only want completion proposals if cursor is after doctype... |
| if (!isCursorAfterDoctype(contentAssistRequest)) { |
| return; |
| } |
| |
| if ((parent != null) && (parent instanceof IDOMNode) && isCommentNode((IDOMNode) parent)) { |
| // loop and find non comment node? |
| while ((parent != null) && isCommentNode((IDOMNode) parent)) { |
| parent = parent.getParentNode(); |
| } |
| } |
| |
| List<String> proposedValues = getProposedValues(context); |
| |
| if (proposedValues.size() > 0 ) { |
| String matchString = contentAssistRequest.getMatchString(); |
| if (matchString == null) { |
| matchString = ""; //$NON-NLS-1$ |
| } |
| |
| int begin = contentAssistRequest.getReplacementBeginPosition(); |
| int length = contentAssistRequest.getReplacementLength(); |
| if ((parent instanceof IDOMNode) && (parent.getNodeType() == Node.ELEMENT_NODE)) { |
| if (((IDOMNode) parent).getLastStructuredDocumentRegion() != ((IDOMNode) parent).getFirstStructuredDocumentRegion()) { |
| begin = ((IDOMNode) parent).getFirstStructuredDocumentRegion().getEndOffset(); |
| length = ((IDOMNode) parent).getLastStructuredDocumentRegion().getStartOffset() - begin; |
| } |
| } |
| |
| for (String possibleValue : proposedValues) { |
| if ((matchString.length() == 0) || StringTools.stringStartsWithIgnoreCase(possibleValue, matchString)) { |
| CustomCompletionProposal proposal = new CustomCompletionProposal( |
| possibleValue, begin, length, possibleValue.length(), |
| JptJpaUiPlugin.getImage(JptUiIcons.JPA_CONTENT), |
| possibleValue, null, null, XMLRelevanceConstants.R_TAG_INSERTION); |
| contentAssistRequest.addProposal(proposal); |
| } |
| } |
| } |
| else { |
| setErrorMessage(XMLUIMessages.Content_Assist_not_availab_UI_); |
| } |
| } |
| |
| /** |
| * Retrieves all of the possible valid values for this attribute/element declaration |
| */ |
| private List<String> getProposedValues(CompletionProposalInvocationContext context) { |
| List<String> list = new ArrayList<String>(); |
| int documentPosition = context.getInvocationOffset(); |
| if (documentPosition == -1) return Collections.emptyList(); |
| |
| ITextFileBufferManager manager = FileBuffers.getTextFileBufferManager(); |
| ITextFileBuffer buffer = manager.getTextFileBuffer(context.getDocument()); |
| if (buffer == null) return Collections.emptyList(); |
| |
| IWorkspaceRoot root = ResourcesPlugin.getWorkspace().getRoot(); |
| IFile file = root.getFile(buffer.getLocation()); |
| |
| JpaFile jpaFile = (JpaFile) file.getAdapter(JpaFile.class); |
| if (jpaFile == null) return Collections.emptyList(); |
| |
| Collection<JpaStructureNode> rootStructureNodes = CollectionTools.collection(jpaFile.getRootStructureNodes()); |
| if (rootStructureNodes.isEmpty()) { |
| return Collections.emptyList(); |
| } |
| |
| for (JpaStructureNode node : rootStructureNodes) { |
| Iterable<String> strings = ((JpaXmlStructureNode) node).getXmlCompletionProposals(documentPosition); |
| if (strings != null) { |
| CollectionTools.addAll(list, strings); |
| } else { |
| return Collections.emptyList(); |
| } |
| |
| } |
| return list; |
| } |
| |
| /** |
| * This method can check if the cursor is after the XMLPI |
| * |
| * @param ContentAssistRequest car |
| */ |
| protected boolean isCursorAfterXMLPI(ContentAssistRequest car) { |
| Node aNode = car.getNode(); |
| boolean xmlpiFound = false; |
| Document parent = aNode.getOwnerDocument(); |
| int xmlpiNodePosition = -1; |
| boolean isAfterXMLPI = false; |
| |
| if (parent == null) { |
| return true; // blank document case |
| } |
| |
| for (Node child = parent.getFirstChild(); child != null; child = child.getNextSibling()) { |
| boolean xmlpi = ((child.getNodeType() == Node.PROCESSING_INSTRUCTION_NODE) && child.getNodeName().equals("xml")); //$NON-NLS-1$ |
| xmlpiFound = xmlpiFound || xmlpi; |
| if (xmlpiFound) { |
| if (child instanceof IDOMNode) { |
| xmlpiNodePosition = ((IDOMNode) child).getEndOffset(); |
| isAfterXMLPI = (car.getReplacementBeginPosition() >= xmlpiNodePosition); |
| } |
| break; |
| } |
| } |
| return isAfterXMLPI; |
| } |
| |
| private boolean isCursorAfterDoctype(ContentAssistRequest car) { |
| Node aNode = car.getNode(); |
| Document parent = aNode.getOwnerDocument(); |
| int xmldoctypeNodePosition = -1; |
| boolean isAfterDoctype = true; |
| |
| if (parent == null) { |
| return true; // blank document case |
| } |
| |
| for (Node child = parent.getFirstChild(); child != null; child = child.getNextSibling()) { |
| if (child instanceof IDOMNode) { |
| if (child.getNodeType() == Node.DOCUMENT_TYPE_NODE) { |
| xmldoctypeNodePosition = ((IDOMNode) child).getEndOffset(); |
| isAfterDoctype = (car.getReplacementBeginPosition() >= xmldoctypeNodePosition); |
| break; |
| } |
| } |
| } |
| return isAfterDoctype; |
| } |
| |
| /** |
| * This is to determine if a tag is a special meta-info comment tag that |
| * shows up as an ELEMENT |
| * |
| * @param node |
| */ |
| private boolean isCommentNode(IDOMNode node) { |
| return ((node != null) && (node instanceof IDOMElement) && ((IDOMElement) node).isCommentTag()); |
| } |
| } |