/******************************************************************************* * Copyright (c) 2013 AKSW Xturtle Project, itemis AG (http://www.itemis.eu). * All rights reserved. This program and the accompanying materials * are 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 ******************************************************************************/ /* * generated by Xtext */ package de.itemis.tooling.xturtle.ui.contentassist; import java.util.ArrayList; import java.util.List; import org.eclipse.emf.common.util.EList; import org.eclipse.emf.ecore.EObject; import org.eclipse.jface.text.contentassist.ICompletionProposal; import org.eclipse.jface.viewers.StyledString; import org.eclipse.xtext.Assignment; import org.eclipse.xtext.EcoreUtil2; import org.eclipse.xtext.Keyword; import org.eclipse.xtext.RuleCall; import org.eclipse.xtext.naming.QualifiedName; import org.eclipse.xtext.nodemodel.util.NodeModelUtils; import org.eclipse.xtext.resource.IEObjectDescription; import org.eclipse.xtext.ui.editor.contentassist.ConfigurableCompletionProposal; import org.eclipse.xtext.ui.editor.contentassist.ContentAssistContext; import org.eclipse.xtext.ui.editor.contentassist.ICompletionProposalAcceptor; import org.eclipse.xtext.ui.editor.contentassist.PrefixMatcher; import com.google.common.base.Function; import com.google.common.base.Optional; import com.google.common.base.Predicate; import com.google.inject.Inject; import de.itemis.tooling.xturtle.resource.TurtleResourceService; import de.itemis.tooling.xturtle.services.Prefixes; import de.itemis.tooling.xturtle.services.XturtleGrammarAccess; import de.itemis.tooling.xturtle.xturtle.BlankObjects; import de.itemis.tooling.xturtle.xturtle.Object; import de.itemis.tooling.xturtle.xturtle.PredicateObjectList; import de.itemis.tooling.xturtle.xturtle.PrefixId; import de.itemis.tooling.xturtle.xturtle.QNameRef; import de.itemis.tooling.xturtle.xturtle.StringLiteral; import de.itemis.tooling.xturtle.xturtle.Subject; import de.itemis.tooling.xturtle.xturtle.Triples; import de.itemis.tooling.xturtle.xturtle.XturtlePackage; /** * see http://www.eclipse.org/Xtext/documentation/latest/xtext.html#contentAssist on how to customize content assistant */ public class XturtleProposalProvider extends AbstractXturtleProposalProvider { @Inject TurtleResourceService service; @Inject XturtleGrammarAccess ga; @Inject Prefixes prefixes; @Inject TurtleLiteralsLanguages languages; //prefixMatcher only for ColonNames!! private PrefixMatcher subStringMatcher=new PrefixMatcher(){ CamelCase delegate=new CamelCase(); @Override public boolean isCandidateMatchingPrefix(String name, String prefix) { return name.toLowerCase().contains(prefix.substring(1).toLowerCase()) || delegate.isCandidateMatchingPrefix(name, prefix); }}; private class LabelPrefixMatcher extends PrefixMatcher{ private String label; public LabelPrefixMatcher(String forLabel) { label=forLabel.toLowerCase(); } @Override public boolean isCandidateMatchingPrefix(String name, String prefix) { return label.contains(prefix.substring(1).toLowerCase()); } } @Override protected String getDisplayString(EObject element, String qualifiedNameAsString, String shortName) { if(element instanceof PrefixId){ StringBuilder result=new StringBuilder(Optional.fromNullable(((PrefixId) element).getId()).or("")); result.append(": - ").append(((PrefixId) element).getUri()); return result.toString(); } return super.getDisplayString(element, qualifiedNameAsString, shortName); } private PrefixId getPrefixId(ContentAssistContext context, EObject model, String caPrefix){ EObject model2=model; if(model2 instanceof QNameRef){ PrefixId referencedPrefix = ((QNameRef)model2).getPrefix(); if(referencedPrefix!=null && !referencedPrefix.eIsProxy()){ return referencedPrefix; } } return null; } @Override public void completeQNameRef_Ref(EObject model, Assignment assignment, final ContentAssistContext context, ICompletionProposalAcceptor acceptor) { String prefix=context.getPrefix(); if(prefix==null || prefix.length()==0 || prefix.charAt(0)!=':'){ return; } //the following seems to be the case if code completion is invoked on an empty prefix //in this case getting the previous model returns the actual node to be completed!? if(model instanceof PredicateObjectList){ model=context.getPreviousModel(); } PrefixId referencedPrefix=getPrefixId(context, model, prefix); if(referencedPrefix==null){ return; } final String prefixURI=service.getUriString(referencedPrefix); final ContentAssistContext newContext=context.copy().setMatcher(subStringMatcher).toContext(); // final QualifiedName currentQName = service.getQualifiedName(model); final List<IEObjectDescription> additionalProposals=new ArrayList<IEObjectDescription>(); ReferenceProposalCreator creator = getCrossReferenceProposalCreator(); Predicate<IEObjectDescription>predicate=new Predicate<IEObjectDescription>() { public boolean apply(IEObjectDescription object){ // boolean nsMatches=object.getQualifiedName().getFirstSegment().equals(currentQName.getFirstSegment()); boolean prefixMatches=object.getQualifiedName().toString("").startsWith(prefixURI); if(prefixMatches){ String label=object.getUserData("label"); if(label!=null){ additionalProposals.add(object); } } return prefixMatches; } }; Function<IEObjectDescription, ICompletionProposal> factory = new Function<IEObjectDescription, ICompletionProposal>() { public ICompletionProposal apply(IEObjectDescription desc){ String fullName=desc.getQualifiedName().toString(""); String proposeName=fullName.substring(prefixURI.length()); return createCompletionProposal(":"+proposeName, newContext); } }; creator.lookupCrossReference((EObject)model, XturtlePackage.Literals.RESOURCE_REF__REF, acceptor, predicate, factory); int additionalProposalPriority=getPriorityHelper().getDefaultPriority()-1; for (IEObjectDescription additional : additionalProposals) { String fullName=additional.getQualifiedName().toString(""); String proposeName=fullName.substring(prefixURI.length()); String[] labels = additional.getUserData("label").split(",,"); String matchString="\"{1,3}"+proposeName+"\"{1,3}"; for (String label : labels) { boolean labelAndPropsalIdentical=label.matches(matchString); if(!labelAndPropsalIdentical){ acceptor.accept( createCompletionProposal( ":"+proposeName, new StyledString(label).append(" - ").append(proposeName), null, additionalProposalPriority, context.getPrefix(), context.copy().setMatcher(new LabelPrefixMatcher(label)).toContext())); } } } // super.completeQNameRef_Ref(model, assignment, context, acceptor); } //complete URIs only if < has already been entered @Override public void complete_UriRef(EObject model, RuleCall ruleCall, ContentAssistContext context, ICompletionProposalAcceptor acceptor) { String prefix=context.getPrefix(); if(prefix==null || prefix.length()==0 || prefix.charAt(0)!='<'){ return; } super.complete_UriRef(model, ruleCall, context, acceptor); } //complete URIs only if < has already been entered @Override public void completeUriRef_Ref(EObject model, Assignment assignment, ContentAssistContext context, ICompletionProposalAcceptor acceptor) { String prefix=context.getPrefix(); if(prefix==null || prefix.length()==0 || prefix.charAt(0)!='<'){ return; } super.completeUriRef_Ref(model, assignment, context, acceptor); } //fetch the most popular URI from prefix.cc @Override public void completePrefixId_Uri(EObject model, Assignment assignment, ContentAssistContext context, ICompletionProposalAcceptor acceptor) { if(model instanceof PrefixId){ String id=((PrefixId)model).getId(); List<String> uri=prefixes.getUris(id); if(uri!=null){ acceptor.accept(createCompletionProposal("<"+uri.get(0)+">", context)); } } } //propose prefix ids known to prefix.cc @Override public void completePrefixId_Id(EObject model, Assignment assignment, ContentAssistContext context, ICompletionProposalAcceptor acceptor) { for (String prefix : prefixes.getPrefixes()) { String proposal = prefix;//+":<"+prefixes.getUri(prefix)+">"; acceptor.accept(createCompletionProposal(proposal,proposal+" - "+prefixes.getUris(prefix).get(0),null, context)); } } private class ColonAddingAcceptor implements ICompletionProposalAcceptor{ private ICompletionProposalAcceptor delegate; public ColonAddingAcceptor(ICompletionProposalAcceptor delegate) { this.delegate=delegate; } public void accept(ICompletionProposal proposal) { if(proposal instanceof ConfigurableCompletionProposal){ ConfigurableCompletionProposal newProposal = (ConfigurableCompletionProposal) proposal; newProposal.setReplacementString(newProposal.getReplacementString()+":"); newProposal.setReplaceContextLength(newProposal.getReplaceContextLength()+1); newProposal.setCursorPosition(newProposal.getCursorPosition()+1); } delegate.accept(proposal); } public boolean canAcceptMoreProposals() { return delegate.canAcceptMoreProposals(); } } //colon is not part of the prefix, but we want to add it automatically //so the user does not have to do it, the implemented hack avoids another //colon to be added in case there already is one @Override public void completeQNameRef_Prefix(EObject model, Assignment assignment, ContentAssistContext context, final ICompletionProposalAcceptor acceptor) { ICompletionProposalAcceptor colonAddingAcceptor=acceptor; boolean noWhiteSpace=(context.getOffset()==context.getLastCompleteNode().getTotalEndOffset()); if(noWhiteSpace){ return; } if(!context.getCurrentNode().getText().contains(":")){ colonAddingAcceptor=new ColonAddingAcceptor(acceptor); } super.completeQNameRef_Prefix(model, assignment, context, colonAddingAcceptor); } @Override public void completeQNameDef_Prefix(EObject model, Assignment assignment, ContentAssistContext context, ICompletionProposalAcceptor acceptor) { ICompletionProposalAcceptor colonAddingAcceptor=acceptor; if(!context.getCurrentNode().getText().contains(":")){ colonAddingAcceptor=new ColonAddingAcceptor(acceptor); } super.completeQNameDef_Prefix(model, assignment, context, colonAddingAcceptor); } @Override public void complete_StringLiteral(EObject model, RuleCall ruleCall, ContentAssistContext context, ICompletionProposalAcceptor acceptor) { String prefix = context.getPrefix(); if(prefix==null || prefix.length()==0 || prefix.charAt(0)!='"'){ //code completion only if it is clear that a string literal is started return; } //propose string literals from the current file whose predicate URI matches that of the current predicate PredicateObjectList objList = EcoreUtil2.getContainerOfType(model, PredicateObjectList.class); if(objList!=null){ de.itemis.tooling.xturtle.xturtle.Predicate verb = objList.getVerb(); QualifiedName name = service.getQualifiedName(verb); if(name!=null){ EObject root = EcoreUtil2.getRootContainer(model); List<de.itemis.tooling.xturtle.xturtle.Predicate> candidates = EcoreUtil2.getAllContentsOfType(root, de.itemis.tooling.xturtle.xturtle.Predicate.class); for (de.itemis.tooling.xturtle.xturtle.Predicate predicate : candidates) { if(name.equals(service.getQualifiedName(predicate))){ EObject container = predicate.eContainer(); if(container instanceof PredicateObjectList){ EList<Object> objects = ((PredicateObjectList)predicate.eContainer()).getObjects(); for (Object object : objects) { if(object instanceof StringLiteral){ acceptor.accept(createCompletionProposal(((StringLiteral) object).getValue(), context)); } } } } } } } } @Override public void completeStringLiteral_Language(EObject model, Assignment assignment, ContentAssistContext context, ICompletionProposalAcceptor acceptor) { for (String language : languages.getLanguagesToPropose()) { acceptor.accept(createCompletionProposal("@"+language, context)); } } public void complete_TRIPELEND(Triples model, RuleCall ruleCall, ContentAssistContext context, ICompletionProposalAcceptor acceptor) { boolean isAxiom=model.getSubject() instanceof BlankObjects; boolean predicateNonEmpty=!model.getPredObjs().isEmpty(); boolean doPropose=false; if(isAxiom){ doPropose=true; }else if(predicateNonEmpty){ EObject semanticNode = NodeModelUtils.findActualSemanticObjectFor(context.getLastCompleteNode()); if(!(semanticNode instanceof Subject)){ doPropose=true; } } if(doPropose){ super.completeKeyword(ga.getNameAccess().getFullStopKeyword_1_0_0(), context, acceptor); } } @Override public void completeKeyword(Keyword keyword, ContentAssistContext contentAssistContext, ICompletionProposalAcceptor acceptor) { char firstCharacter=keyword.getValue().charAt(0); if(firstCharacter!='.'){ boolean wsBeforeKeywordRequired; switch (firstCharacter){ case ',': case ':': case ';':wsBeforeKeywordRequired=false; break; default: wsBeforeKeywordRequired=true; } int offset = contentAssistContext.getOffset(); int lastNodeEndOffset=contentAssistContext.getLastCompleteNode().getTotalEndOffset(); boolean startOfDocument=offset-contentAssistContext.getPrefix().length()==0; boolean wsAfterLastCompleteNode=startOfDocument || offset>lastNodeEndOffset; if(!wsBeforeKeywordRequired || wsAfterLastCompleteNode){ super.completeKeyword(keyword, contentAssistContext, acceptor); } } } }