blob: 7af9e6c4973d3b2da9ac5adaf9e6ee1431140076 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2011, 2013 Oracle. All rights reserved.
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License 2.0 and Eclipse Distribution License v. 1.0
* which accompanies this distribution.
* The Eclipse Public License is available at https://www.eclipse.org/legal/epl-2.0/
*
* SPDX-License-Identifier: EPL-2.0
* 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.jpql;
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.resource.ResourceManager;
import org.eclipse.jface.text.BadLocationException;
import org.eclipse.jface.text.IDocument;
import org.eclipse.jface.text.contentassist.ICompletionProposal;
import org.eclipse.jface.text.contentassist.IContextInformation;
import org.eclipse.jpt.common.core.utility.TextRange;
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.context.PersistentType;
import org.eclipse.jpt.jpa.core.context.TypeMapping;
import org.eclipse.jpt.jpa.core.context.orm.EntityMappings;
import org.eclipse.jpt.jpa.core.context.orm.OrmEntity;
import org.eclipse.jpt.jpa.core.context.orm.OrmNamedQuery;
import org.eclipse.jpt.jpa.core.context.orm.OrmQueryContainer;
import org.eclipse.jpt.jpa.ui.JptJpaUiMessages;
import org.eclipse.jpt.jpa.ui.internal.plugin.JptJpaUiPlugin;
import org.eclipse.persistence.jpa.jpql.tools.utility.XmlEscapeCharacterConverter;
import org.eclipse.swt.graphics.Image;
import org.eclipse.wst.sse.ui.contentassist.CompletionProposalInvocationContext;
import org.eclipse.wst.sse.ui.contentassist.ICompletionProposalComputer;
/**
* This computer adds content assist support when it is invoked inside the <query> element
* defined in a mapping file (ORM Configuration).
*
* @version 3.3
* @since 3.0
* @author Pascal Filion
*/
public final class JpaJpqlSseCompletionProposalComputer extends JpqlCompletionProposalComputer<ICompletionProposal>
implements ICompletionProposalComputer {
private boolean cDATASection;
/**
* Creates a new <code>JpaJpqlSseCompletionProposalComputer</code>.
*/
public JpaJpqlSseCompletionProposalComputer() {
super();
}
/**
* {@inheritDoc}
*/
@Override
ICompletionProposal buildProposal(String proposal,
String displayString,
String additionalInfo,
Image image,
int cursorOffset) {
return new JpqlSseCompletionProposal(
contentAssistProposals,
proposal,
displayString,
additionalInfo,
image,
namedQuery,
jpqlQuery,
actualQuery,
tokenStart,
tokenEnd,
position,
actualPosition,
cursorOffset,
cDATASection
);
}
/**
* {@inheritDoc}
*/
public List<ICompletionProposal> computeCompletionProposals(CompletionProposalInvocationContext context,
IProgressMonitor monitor) {
monitor.beginTask(null, 100);
try {
int offset = context.getInvocationOffset();
if (offset == -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();
monitor.worked(80);
checkCanceled(monitor);
// Retrieve the JPA's model object
int[] position = new int[1];
OrmNamedQuery namedQuery = namedQuery(jpaFile, offset, position);
if (namedQuery == null) return Collections.emptyList();
// Retrieve the entire JPQL query from the document
Object[] info = jpqlQuery(namedQuery, context.getDocument());
String jpqlQuery = (String) info[0];
this.cDATASection = (Boolean) info[1];
// Keep track of the beginning and ending offsets since the entire string is always replaced
int tokenStart = offset - position[0];
int tokenEnd = tokenStart + jpqlQuery.length();
// Now create the proposals
ResourceManager resourceManager = this.getResourceManager(context.getViewer().getTextWidget());
return buildProposals(namedQuery, jpqlQuery, tokenStart, tokenEnd, position[0], resourceManager);
}
catch (Exception ex) {
JptJpaUiPlugin.instance().logError(ex, JptJpaUiMessages.JPA_JPQL_SSE_COMPLETION_PROPOSAL_COMPUTER_ERROR);
}
finally {
monitor.done();
}
return Collections.emptyList();
}
/**
* {@inheritDoc}
*/
public List<IContextInformation> computeContextInformation(CompletionProposalInvocationContext context,
IProgressMonitor monitor) {
return Collections.emptyList();
}
private OrmNamedQuery findNamedQuery(JpaStructureNode structureNode, int offset, int[] position) {
if (structureNode instanceof EntityMappings) {
EntityMappings entityMappings = (EntityMappings) structureNode;
// First look into the global queries
OrmNamedQuery query = findNamedQuery(entityMappings.getQueryContainer(), offset, position);
// Now traverse each entity
if (query != null) {
return query;
}
for (PersistentType persistentType : entityMappings.getPersistentTypes()) {
TypeMapping typeMapping = persistentType.getMapping();
if (typeMapping instanceof OrmEntity) {
query = findNamedQuery(((OrmEntity) typeMapping).getQueryContainer(), offset, position);
if (query != null) {
return query;
}
}
}
}
return null;
}
private OrmNamedQuery findNamedQuery(OrmQueryContainer container, int offset, int[] position) {
for (OrmNamedQuery namedQuery : container.getNamedQueries()) {
for (TextRange textRange : namedQuery.getQueryTextRanges()) {
if (textRange.touches(offset)) {
position[0] = offset - textRange.getOffset();
return namedQuery;
}
}
}
return null;
}
private Object[] jpqlQuery(OrmNamedQuery namedQuery, IDocument document) {
try {
TextRange range = namedQuery.getQueryTextRanges().get(0);
return new Object[] {
document.get(range.getOffset(), range.getLength()),
namedQuery.getXmlQuery().isQueryInsideCDATASection()
};
}
catch (BadLocationException e) {
return new Object[] {
StringTools.EMPTY_STRING,
Boolean.FALSE
};
}
}
/**
* {@inheritDoc}
*/
@Override
String modifyJpqlQuery(String jpqlQuery, int[] position) {
// The JPQL query is not encapsulated by a CDATA section,
// converts any escape characters like &lt; into '<'
if (!cDATASection) {
jpqlQuery = XmlEscapeCharacterConverter.unescape(jpqlQuery, position);
}
return jpqlQuery;
}
private OrmNamedQuery namedQuery(JpaFile jpaFile, int offset, int[] position) {
for (JpaStructureNode node : jpaFile.getRootStructureNodes()) {
OrmNamedQuery namedQuery = findNamedQuery(node, offset, position);
if (namedQuery != null) {
return namedQuery;
}
}
return null;
}
}