/*-
* Copyright (C) 2011-2014 by Iwao AVE!
* This program is made available under the terms of the MIT License.
*/
package org.eclipselabs.stlipse.jspeditor;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.resources.IWorkspaceRoot;
import org.eclipse.core.resources.ResourcesPlugin;
import org.eclipse.core.runtime.IPath;
import org.eclipse.core.runtime.Path;
import org.eclipse.jdt.core.IJavaProject;
import org.eclipse.jdt.core.JavaCore;
import org.eclipse.jface.text.IDocument;
import org.eclipse.jface.text.contentassist.CompletionProposal;
import org.eclipse.jface.text.contentassist.ICompletionProposal;
import org.eclipse.wst.sse.core.StructuredModelManager;
import org.eclipse.wst.sse.core.internal.provisional.IStructuredModel;
import org.eclipse.wst.sse.core.internal.provisional.text.IStructuredDocumentRegion;
import org.eclipse.wst.sse.core.internal.provisional.text.ITextRegion;
import org.eclipse.wst.sse.core.internal.provisional.text.ITextRegionList;
import org.eclipse.wst.sse.core.utils.StringUtils;
import org.eclipse.wst.sse.ui.contentassist.CompletionProposalInvocationContext;
import org.eclipse.wst.xml.core.internal.provisional.document.IDOMNode;
import org.eclipse.wst.xml.core.internal.regions.DOMRegionContext;
import org.eclipse.wst.xml.ui.internal.contentassist.AbstractXMLCompletionProposalComputer;
import org.eclipse.wst.xml.ui.internal.contentassist.ContentAssistRequest;
import org.eclipse.wst.xml.ui.internal.contentassist.DefaultXMLCompletionProposalComputer;
import org.eclipselabs.stlipse.Activator;
import org.eclipselabs.stlipse.cache.BeanClassCache;
import org.eclipselabs.stlipse.cache.BeanClassInfo;
import org.eclipselabs.stlipse.cache.BeanPropertyCache;
import org.eclipselabs.stlipse.javaeditor.JavaCompletionProposal;
import org.eclipselabs.stlipse.util.ClassNameUtil;
/**
* The super class {@link AbstractXMLCompletionProposalComputer} will be available to API.
* https://bugs.eclipse.org/bugs/show_bug.cgi?id=310696
*
* @author ave
*/
@SuppressWarnings("restriction")
public class JspCompletionProposalComputer extends DefaultXMLCompletionProposalComputer
{
protected void addAttributeValueProposals(ContentAssistRequest contentAssistRequest,
CompletionProposalInvocationContext context)
{
IDOMNode node = (IDOMNode)contentAssistRequest.getNode();
String tagName = node.getNodeName();
IStructuredDocumentRegion open = node.getFirstStructuredDocumentRegion();
ITextRegionList openRegions = open.getRegions();
int i = openRegions.indexOf(contentAssistRequest.getRegion());
if (i < 0)
return;
ITextRegion nameRegion = null;
while (i >= 0)
{
nameRegion = openRegions.get(i--);
if (nameRegion.getType() == DOMRegionContext.XML_TAG_ATTRIBUTE_NAME)
break;
}
// get the attribute in question (first attr name to the left of the cursor)
String attributeName = null;
if (nameRegion != null)
attributeName = open.getText(nameRegion);
if (isProposalsAvailable(tagName, attributeName))
{
// boolean existingComplicatedValue = contentAssistRequest.getRegion() != null
// && contentAssistRequest.getRegion() instanceof ITextRegionContainer;
// if (existingComplicatedValue)
// {
// contentAssistRequest.getProposals().clear();
// contentAssistRequest.getMacros().clear();
// return;
// }
String currentValue = null;
if (contentAssistRequest.getRegion().getType() == DOMRegionContext.XML_TAG_ATTRIBUTE_VALUE)
currentValue = contentAssistRequest.getText();
else
currentValue = "";
String matchString = null;
int start = contentAssistRequest.getReplacementBeginPosition();
int length = contentAssistRequest.getReplacementLength();
if (currentValue.length() > StringUtils.strip(currentValue).length()
&& (currentValue.startsWith("\"") || currentValue.startsWith("'"))
&& contentAssistRequest.getMatchString().length() > 0)
{
// Value is surrounded by (double) quotes.
matchString = currentValue.substring(1, contentAssistRequest.getMatchString().length());
start++;
length = currentValue.length() - 2;
}
else
matchString = currentValue.substring(0, contentAssistRequest.getMatchString().length());
if ("beanclass".equalsIgnoreCase(attributeName))
{
proposeBeanclass(contentAssistRequest, context, matchString, start, length);
}
else if (StripesTagUtil.isSuggestableFormTag(tagName, attributeName))
{
String beanclass = StripesTagUtil.getParentBeanclass(node, "form");
boolean includeReadOnly = "label".equals(StripesTagUtil.getStripesTagSuffix(tagName));
proposalField(contentAssistRequest, matchString, start, length, beanclass,
includeReadOnly);
}
else if (StripesTagUtil.isParamTag(tagName, attributeName))
{
String beanclass = StripesTagUtil.getParentBeanclass(node, "url", "link");
proposalField(contentAssistRequest, matchString, start, length, beanclass, false);
}
else if (StripesTagUtil.isSubmitTag(tagName, attributeName))
{
String beanclass = StripesTagUtil.getParentBeanclass(node, "form");
proposeEvent(contentAssistRequest, node, matchString, start, length, beanclass);
}
else if (StripesTagUtil.isEventAttribute(tagName, attributeName))
{
String beanclass = StripesTagUtil.getBeanclassAttribute(node);
proposeEvent(contentAssistRequest, node, matchString, start, length, beanclass);
}
}
}
private void proposeEvent(ContentAssistRequest contentAssistRequest, IDOMNode node,
String matchString, int start, int length, String beanclass)
{
if (beanclass != null)
{
IResource resource = getResource(contentAssistRequest);
IJavaProject project = getJavaProject(resource);
List<String> events = BeanPropertyCache.searchEventHandler(project, beanclass,
matchString, false, false);
int relevance = events.size();
for (String event : events)
{
ICompletionProposal proposal = new JavaCompletionProposal(event, start, length,
event.length(), Activator.getIcon(), event, null, null, relevance--);
contentAssistRequest.addProposal(proposal);
}
}
}
private void proposalField(ContentAssistRequest contentAssistRequest, String matchString,
int start, int length, String beanclass, boolean includeReadOnly)
{
if (beanclass != null)
{
IResource resource = getResource(contentAssistRequest);
IJavaProject project = getJavaProject(resource);
Map<String, String> fields = BeanPropertyCache.searchFields(project, beanclass,
matchString, includeReadOnly, -1, false);
List<ICompletionProposal> proposals = BeanPropertyCache.buildFieldNameProposal(fields,
matchString, start, length);
for (ICompletionProposal proposal : proposals)
{
contentAssistRequest.addProposal(proposal);
}
}
}
private void proposeBeanclass(ContentAssistRequest contentAssistRequest,
CompletionProposalInvocationContext context, String matchString, int start, int length)
{
List<ICompletionProposal> classProposals = getBeanclassProposals(context,
contentAssistRequest, matchString, start, length);
if (classProposals != null)
{
for (ICompletionProposal proposal : classProposals)
{
contentAssistRequest.addProposal(proposal);
}
}
}
protected static List<ICompletionProposal> getBeanclassProposals(
CompletionProposalInvocationContext context, ContentAssistRequest contentAssistRequest,
final String input, final int offset, final int replacementLength)
{
final List<ICompletionProposal> proposalList = new ArrayList<ICompletionProposal>();
final IResource resource = getResource(contentAssistRequest);
final String packageName = ClassNameUtil.getPackage(input);
final String typeName = ClassNameUtil.getTypeName(input);
final List<BeanClassInfo> beanClassList = BeanClassCache.getBeanClassInfo(getJavaProject(resource));
synchronized (beanClassList)
{
for (BeanClassInfo beanClass : beanClassList)
{
if (beanClass.matches(packageName.toCharArray(), typeName.toCharArray()))
{
StringBuilder replacementString = new StringBuilder();
replacementString.append(beanClass.getPackageName())
.append('.')
.append(beanClass.getSimpleTypeName());
StringBuilder displayString = new StringBuilder();
displayString.append(beanClass.getSimpleTypeName())
.append(" - ")
.append(beanClass.getPackageName());
int replacementOffset = offset;
int cursorPosition = replacementString.length();
ICompletionProposal proposal = new CompletionProposal(replacementString.toString(),
replacementOffset, replacementLength, cursorPosition, Activator.getIcon(),
displayString.toString(), null, null);
proposalList.add(proposal);
}
}
}
return proposalList;
}
private static IJavaProject getJavaProject(IResource resource)
{
IProject proj = resource.getProject();
IJavaProject javaProject = JavaCore.create(proj);
return javaProject;
}
private static IResource getResource(ContentAssistRequest request)
{
IResource resource = null;
String baselocation = null;
if (request != null)
{
IStructuredDocumentRegion region = request.getDocumentRegion();
if (region != null)
{
IDocument document = region.getParentDocument();
IStructuredModel model = null;
try
{
model = StructuredModelManager.getModelManager().getExistingModelForRead(document);
if (model != null)
{
baselocation = model.getBaseLocation();
}
}
finally
{
if (model != null)
model.releaseFromRead();
}
}
}
if (baselocation != null)
{
IWorkspaceRoot root = ResourcesPlugin.getWorkspace().getRoot();
IPath filePath = new Path(baselocation);
IFile file = null;
if (filePath.segmentCount() > 1)
{
file = root.getFile(filePath);
}
if (file != null)
{
resource = file.getProject();
}
}
return resource;
}
private boolean isProposalsAvailable(String tagName, String attributeName)
{
return "beanclass".equalsIgnoreCase(attributeName)
|| StripesTagUtil.isSuggestableFormTag(tagName, attributeName)
|| StripesTagUtil.isParamTag(tagName, attributeName)
|| StripesTagUtil.isSubmitTag(tagName, attributeName)
|| StripesTagUtil.isEventAttribute(tagName, attributeName);
}
}