Package org.cfeclipse.cfml.editors

Source Code of org.cfeclipse.cfml.editors.CFCompletionProcessor$Validator

/*
* Created on Jan 22, 2004
*
* The MIT License
* Copyright (c) 2004 Rob Rohan
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the Software
* is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package org.cfeclipse.cfml.editors;

import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;
import java.util.StringTokenizer;
import java.util.TreeSet;

import org.cfeclipse.cfml.dictionary.DictionaryManager;
import org.cfeclipse.cfml.dictionary.Function;
import org.cfeclipse.cfml.dictionary.ISyntaxDictionary;
import org.cfeclipse.cfml.dictionary.Parameter;
import org.cfeclipse.cfml.dictionary.SyntaxDictionary;
import org.cfeclipse.cfml.dictionary.Value;
import org.cfeclipse.cfml.editors.contentassist.CFEContentAssist;
import org.cfeclipse.cfml.editors.partitioner.scanners.CFPartitionScanner;
import org.cfeclipse.cfml.util.CFPluginImages;
import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.IFolder;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IPath;
import org.eclipse.jface.text.BadLocationException;
import org.eclipse.jface.text.FindReplaceDocumentAdapter;
import org.eclipse.jface.text.IDocument;
import org.eclipse.jface.text.IRegion;
import org.eclipse.jface.text.ITextViewer;
import org.eclipse.jface.text.TextPresentation;
import org.eclipse.jface.text.contentassist.ContentAssistant;
import org.eclipse.jface.text.contentassist.ContextInformation;
import org.eclipse.jface.text.contentassist.ICompletionProposal;
import org.eclipse.jface.text.contentassist.IContentAssistProcessor;
import org.eclipse.jface.text.contentassist.IContextInformation;
import org.eclipse.jface.text.contentassist.IContextInformationPresenter;
import org.eclipse.jface.text.contentassist.IContextInformationValidator;


/**
* @author Rob
*
* This is a simple completion processor (also called code insight) it shows the
* little window with possible completion suggestions. - handles Tags, attributes,
* and functions for the coldfusion language only (at present).
*/
public class CFCompletionProcessor implements IContentAssistProcessor {
  /** tag type */
  private static final short TAGTYPE = 0;
  /** attribute type */
  private static final short ATTRTYPE = 1;
  /** value type */
  private static final short VALUETYPE = 2;
  /** scope type */
  private static final short SCOPETYPE = 3;
 
  protected IContextInformationValidator validator = new Validator();
 
  /**
   * Startup the completer
   */
  public CFCompletionProcessor(ContentAssistant assistant){
    //this.assistant = assistant;
  }

 
  /*
   * getAttributeValueProposals
   *
   * @param syntax The SyntaxDictionary to lookup from
   * @param inputText The input string that we're analysing. Should start at the start of the relevant tag
   * @param indexOfFirstSpace The index of the first space in the string (really should work this out)
   * @param docOffset Offset in the document that the activation occurred at.
   * 
   * @author Oliver Tupman
   */
  protected ICompletionProposal[] getAttributeValueProposals(
    SyntaxDictionary syntax, String inputText, int indexOfFirstSpace,
    int docOffset, IDocument doc) {
   
    int lastSpace = inputText.lastIndexOf(" ");
    int quotes = inputText.lastIndexOf("\"");
    String valueSoFar = "";
   
    if(quotes != -1) {
      // Attribute entered, user is typing.
      valueSoFar = inputText.substring(quotes+1, inputText.length());
    } else {
      quotes = inputText.length() - 2;
    }
   
    String attribute = inputText.substring(lastSpace+1, quotes-1);
    String tag = inputText.substring(0, indexOfFirstSpace);
   
    System.err.println("I think I need to be looking up: " + attribute);
    System.err.println("Tag I think I have is \'" + tag + "\'");

    Set attrProps = ((ISyntaxDictionary)syntax).getFilteredAttributeValues(tag, attribute, valueSoFar);
   
    if(attribute.compareToIgnoreCase("template") == 0) {
      TreeSet suggestions = new TreeSet();
     
      if(doc instanceof ICFDocument) {
        IResource res = ((ICFDocument)doc).getResource();
        IPath folder = res.getFullPath().removeLastSegments(1);
        folder = folder.append(valueSoFar);
        valueSoFar = "";
        IFolder folderRes = res.getWorkspace().getRoot().getFolder(folder);
       
        if(folderRes != null) {
          try {
            IResource children[] = folderRes.members();
            for(int i = 0; i < children.length; i++) {
              if(children[i] instanceof IFolder) {
                suggestions.add(new Value(children[i].getName() + "/"));
              } else if(children[i] instanceof IFile) {
                suggestions.add(new Value(children[i].getName()));
              }
            }
          }catch(CoreException ex) {
            ex.printStackTrace();
          }
        }
       
        attrProps.addAll(suggestions);
      }
    }
   
    if(attrProps != null) {
      if(attrProps.size() > 0 && ((Value)attrProps.toArray()[0]).getValue().compareTo(valueSoFar) == 0)
        return null;
     
      //System.err.println("CFCompletionProcessor::computeCompletionProposals() - I have " + attrProps.size() + " elements available to me");
      return CFEContentAssist.makeSetToProposal(
        attrProps,
        docOffset,
        VALUETYPE,
        valueSoFar.length()
      );
    }
   
    return null;
  }
 
  /**
   * for tag, attribute, value, insight and auto close ... um this might be
   * getting out of control
   * this whole thing is a bit of a hack, but basically it looks at the current
   * partition and tokenizes the contents to the current offset. It then tries
   * to figure out what is being typed and limits the suggested items accordingly
   * ... you know code completion ... :)
   */
  public ICompletionProposal[] computeCompletionProposals(ITextViewer viewer,
    int documentOffset) {
   
    try {
      String limiting = "";      // attributes typed text (if it exists)
      boolean cftag = false;      // assume its not a cftag
      boolean httag = false;      // assume it's not an HTML tag
      String invoker = "";
      IDocument document = viewer.getDocument();
      String currPartitionType = document.getPartition(documentOffset).getType();
      String tagname = "";
      int start = 0;
     
      // Get the text that invoked content assist
      if(documentOffset > 0) {
        invoker = document.get(documentOffset-1,1);
      }
     
      //this is because when they hit > it often moves them into
      //another partiton type - so get the last partition
      if(invoker.equals(">")) {
        start = document.getPartition(documentOffset - 1).getOffset();
      } else if (invoker.equals(".")) {
        FindReplaceDocumentAdapter finder = new FindReplaceDocumentAdapter(document);
        IRegion region = finder.find(documentOffset-2,"[^a-z.]",false,false,false,true);
       
        if (region != null) {
          start = region.getOffset()+1;
        }
      } else {
        start = document.getPartition(documentOffset).getOffset();
        if(currPartitionType.compareToIgnoreCase(CFPartitionScanner.J_SCRIPT) == 0) {
          start+= 8;
        }
      }
     
      String prefix =  eliminateUnwantedChars(document.get(start, documentOffset - start));
     
      ///////////////////////////////////////////////////////////////////
     
      //now go over the whole tag using spaces as the delimiter
      StringTokenizer st = new StringTokenizer(prefix," ");
      StringTokenizer st2 = new StringTokenizer(prefix," ");
     
      //if st has nothing then we got called by mistake or something just
      //bail out
      tagname = (!st.hasMoreTokens()) ? prefix : st.nextToken();
     
      //looks like this is just here to skip, but it causes a
      //java.util.NoSuchElementException sometimes so I am just throwing the
      //hasMore as a quick pactch
      if(st2.hasMoreTokens()) {
        st2.nextToken();
      }
     
      Set attribs = new HashSet();
      String[] fullAttrib;
      String attribName = "";
     
      while(st2.hasMoreTokens()) {
        fullAttrib = st2.nextToken().split("=");
       
        if (fullAttrib.length > 1 && fullAttrib[1].length() > 1) {
          attribName = fullAttrib[0];
          attribs.add(attribName.trim());
        }
      }
     
      //if the tagname has the possibility of being a cf tag
      if(tagname.trim().length() >= 3 && prefix.trim().substring(0,3).equalsIgnoreCase("<cf")) {
        // found a CF tag
        cftag = true;
       
        // Trim to just the lookup key : "cfabort" => "abort"
        tagname = tagname.trim().substring(3);
      } else if(tagname.trim().startsWith("<")){
        // Gets the HTML syntax dictionary (CF tags will have been handled above,
        // therefore they won't have an open chevron)).
        httag = true;
        tagname = tagname.trim().substring(1);
      }
     
      //if this was a <booga> type tag remove the last >
      //so we can look up the tag and see if it needs a closer
      if(tagname.endsWith(">")) {
        tagname = tagname.substring(0,tagname.length()-1);
      }
     
      //if this is an attribute, limiting should have the last
      //entered text (the part we shall filter attributes on)
      while(st.hasMoreTokens()) {
        // in the end should have the thing to limit with
        limiting = st.nextToken();
      }
     
      //if it looks like they have started typing the contents of an
      //attribute (or they are done) set limiting to nothing
      if(limiting.indexOf("\"") > 0 || limiting.indexOf("'") > 0){
        limiting = "";
      }
     
      //if we are in a cftag, and there are no attributes (and we did not
      //start this mess by getting called with a space or tab) then we
      //should lookup cf tag names to suggest
      boolean invokerIsSpace = invoker.equals(" ");
      boolean invokerIsTab = invoker.equals("\t");
      boolean invokerIsCloseChevron = invoker.equals(">");
      boolean invokerIsPeriod = invoker.equals(".");
     
      SyntaxDictionary syntax = null;
     
      if (cftag || invokerIsPeriod) {
        syntax = DictionaryManager.getDictionary(DictionaryManager.CFDIC);
      } else {
        syntax = DictionaryManager.getDictionary(DictionaryManager.HTDIC);
      }
     
      if(limiting.length() <= 0 && !invokerIsSpace && !invokerIsTab && !invokerIsCloseChevron) {
        if(cftag) {
          return lookUpCFTagNames(documentOffset, syntax, invoker, document, prefix);
        } else if(httag) {
          return lookUpTagNames(documentOffset, syntax, invoker, document, prefix);
        } else if (invokerIsPeriod) {
          return lookUpScopeVars(documentOffset, syntax, invoker, document, prefix);
        }
      } else {
        return getAttributeProposals(documentOffset, limiting, syntax, prefix, tagname, attribs);
      }
    } catch(Exception e) {
      //die all quite like.
      e.printStackTrace(System.err);
    }
   
    return null;
  }
 
  /**
   * @param documentOffset
   * @param limiting
   * @param syntax
   * @param prefix
   * @param tagname
   * @return
   */
  private ICompletionProposal[] getAttributeProposals(int documentOffset, String limiting, SyntaxDictionary syntax, String prefix, String tagname, Set attribs) {
    //we are probably in need of attribute in sight
    //clean up the text typed so far
   
    String searchText = limiting.trim();
   
    if (limiting.endsWith("=")) {
      searchText = limiting.substring(0,limiting.length()-1);
    }
   
    // hacks hacks everywhere :) this looks to see if there are an
    // odd number of " in the string prior to this invoke before
    // showing attribute insight. (to keep it from showing attributes
    // inside of attributes)
    String quote_parts[] = prefix.split("\"");
    if(quote_parts.length % 2 != 0)
    {
      // and return our best guess (tagname should have been defined
      // up there ^
      if(syntax != null && prefix.indexOf('>') < 0)
      {
          //This is a nasty hack to filter out the attributes that have already been typed.
          Set proposalSet = syntax.getFilteredAttributes(tagname.trim(),searchText);
          Set toRemove = new HashSet();
          Iterator proposals = proposalSet.iterator();
          Iterator attributes;
          Parameter p;
          while(proposals.hasNext()) {
              p = (Parameter)proposals.next();
              attributes = attribs.iterator();
              while(attributes.hasNext()) {
                  if (attributes.next().toString().equalsIgnoreCase(p.getName())) {
                      toRemove.add(p);
                  }
              }
          }
         
          Iterator removals = toRemove.iterator();
          while (removals.hasNext()) {
              proposalSet.remove(removals.next());
          }
         
         
         
        return CFEContentAssist.makeSetToProposal(
          proposalSet,
          documentOffset,
          ATTRTYPE,
          limiting.length()
        );
      }
    }
    return null;
  }


  /**
   * Content assist comes here if the user is typing out a tag or if the
   * user is now in an attribute. If in an attribute we give out the possible
   * attribute values.
   *
   * @param documentOffset Offset within the document
   * @param syntax Syntax dictionary to use
   * @param invoker The string that invoked the content assist
   * @param document Document that we're working in
   * @param prefix Data that the user has typed
   * @return array of completion proposals, null if no proposals found
   * @throws BadLocationException
   */
  private ICompletionProposal[] lookUpTagNames(int documentOffset, SyntaxDictionary syntax, String invoker, IDocument document, String prefix) throws BadLocationException {
    String taglimiting = prefix.trim().substring(1);
   
    /*////////////////////////// copy from above dup code! //////*/
    if(invoker.charAt(0) == '\"')
    {
      if(document.getChar(documentOffset) == '\"' &&
          document.getChar(documentOffset-2) != '=')
      {  // " entered and there already is one in the document.
        document.replace(documentOffset, 1, "");
        return null;
      }
    }       
   
    // If the taglimiting has a space in we're assuming that the user
    // is intending to input or has inputted some attributes.
    int indexOfFirstSpace = taglimiting.indexOf(" ");
    if(indexOfFirstSpace != -1)
    {
      return getAttributeValueProposals(
          syntax,
          taglimiting,
          indexOfFirstSpace,
          documentOffset, document
      );
    }
    else
    {
    /*////////////////////////// copy from above dup code! //////*/ 
     
      return CFEContentAssist.makeSetToProposal(
        ((ISyntaxDictionary)syntax).getFilteredElements(taglimiting),
        documentOffset,
        TAGTYPE,
        taglimiting.length()
      );
     
    }
  }

 

  private ICompletionProposal[] lookUpScopeVars(int documentOffset, SyntaxDictionary syntax, String invoker, IDocument document, String prefix) throws BadLocationException {
   
// System.out.println("Looking for scope vars with prefix " + prefix);
    int length = prefix.length();
    // If the taglimiting has a space in we're assuming that the user
    // is intending to input or has inputted some attributes.
    Set proposals = ((ISyntaxDictionary)syntax).getFilteredScopeVars(prefix);
   
    Iterator i = proposals.iterator();
   
    while (i.hasNext()) {
        if (i.next() instanceof Function) {
            length = prefix.length() - prefix.lastIndexOf(".");
// System.out.println("Function found in scope lookup. Length reset to " + length);
            break;
        }
    }
   
   
    // Do we have methods in the returned set?
      return CFEContentAssist.makeSetToProposal(
        proposals,
        documentOffset,
        SCOPETYPE,
        length
      );
     
  }



  /**
   * Change any newlines or returns to a space so we can tokenize correctly
   *
   * @param prefix text to work upon
   * @return the cleaned string
   */
  private String eliminateUnwantedChars(String prefix) {
    prefix = prefix.replace('\n',' ');
    prefix = prefix.replace('\r',' ');
    prefix = prefix.replace('\t',' ');
    return prefix;
  }


  /**
   * Content assist comes here if the user is typing out a tag or if the
   * user is now in an attribute. If in an attribute we give out the possible
   * attribute values.
   *
   * @param documentOffset Offset within the document
   * @param syntax Syntax dictionary to use
   * @param invoker The string that invoked the content assist
   * @param document Document that we're working in
   * @param prefix Data that the user has typed
   * @return array of completion proposals, null if no proposals found
   * @throws BadLocationException
   */
  private ICompletionProposal[] lookUpCFTagNames(int documentOffset,
                         SyntaxDictionary syntax,
                         String invoker, IDocument document,
                         String prefix) throws BadLocationException
  {
    //if they have typed more then the cf part get the rest so we
    //can filter out non matches
    String taglimiting = prefix.trim().substring(3);
     
    if(invoker.charAt(0) == '\"')
    {
      // spike@spike.org.uk :: Added code
        // Make sure we aren't at the end of the document
        // before doing the check to see if we have two sets of double quotes.
        //
        if (document.getLength() > documentOffset)
        {
          // spike@spike.org.uk :: Added comment
            // This checks if the invoking charcter is the second of a pair of qoutes
            // and if the first is preceded by an '='. If so we don't want to show
            // insight, so it returns null.
            //
        if(document.getChar(documentOffset) == '\"' &&
           document.getChar(documentOffset-2) != '=')
        {  // " entered and there already is one in the document.
          document.replace(documentOffset, 1, "");
          return null;
        }
        }
    }       
   
    // If the taglimiting has a space in we're assuming that the user
    // is intending to input or has inputted some attributes.
    int indexOfFirstSpace = taglimiting.indexOf(" ");
    if(indexOfFirstSpace != -1)
    {
      return getAttributeValueProposals(
        syntax,
        taglimiting,
        indexOfFirstSpace,
        documentOffset, document
      );
    }
    else
    {
      return CFEContentAssist.makeSetToProposal(
        ((ISyntaxDictionary)syntax).getFilteredElements(taglimiting),
        documentOffset,
        TAGTYPE,
        taglimiting.length()
      );
    }
  }





  /**
   * What characters cause us to wake up (for tags and attributes)
   */
  public char[] getCompletionProposalAutoActivationCharacters() {
    return new char[] { '<', 'f', ' ', 'F', '~', '\t', '\n', '\r', '\"', '.' };
  }

  /**
   * What characters cause us to wake up (for functions / methods)
   */
  public char[] getContextInformationAutoActivationCharacters() {
    return new char[] { '(', '.',',' };
    //return null;
  }
  /** 
   * Shows a little popup that stays active until the class at the bottom of
   * the file says its not
   */
  public IContextInformationValidator getContextInformationValidator() {
    //return null;
    return validator;
  }
 
  /**
   * for functions insight
   */
  public IContextInformation[] computeContextInformation(ITextViewer viewer,
    int documentOffset) {
   
    //System.out.println("context do dad running");
   
    boolean insideFunction = false;
   
    try
    {
      //find out the line number and get the begining of the line
      int linenum = viewer.getDocument().getLineOfOffset(documentOffset);
      int linestart = viewer.getDocument().getLineOffset(linenum);
      //get the line
      String currentline = viewer.getDocument().get(linestart,documentOffset - linestart);
      // Check if we could be inside a function
      if (currentline.lastIndexOf("(") > 0) {
        // Ensure there isn't a closing bracket
        if (currentline.lastIndexOf(")") == -1 || currentline.lastIndexOf(")") < currentline.lastIndexOf("(")) {
          currentline = currentline.substring(0,currentline.lastIndexOf("("));
          insideFunction = true;
        }
      }
      //make it a space delimited list
      currentline = currentline.replace('\"',' ');
      currentline = currentline.replace('\'',' ');
      currentline = currentline.replace('#',' ');
      currentline = currentline.replace('(',' ');
      currentline = currentline.replace(')',' ');

      //tokenize the bad boy
      StringTokenizer st = new StringTokenizer(currentline," ");
     
      //the last item should be our function (because of documentOffset)
      String functionname = "";
      while(st.hasMoreTokens())
      {
        functionname = st.nextToken();
      }
     
      if (insideFunction) {
        functionname += "(";
      }
     
      //remove the last char (which should be the '(')
      //if(functionname.length() > 1)
      //  functionname = functionname.substring(0,functionname.length() - 1);
     
      //System.out.println("f>>" + functionname.trim() + "<<f");
     
      //System.err.println(functionname.trim());
      SyntaxDictionary syntax = DictionaryManager.getDictionary(DictionaryManager.CFDIC);
     
      Set fst = syntax.getFunctions();
      fst = SyntaxDictionary.limitSet(fst,functionname.trim());
     
      //Function fun = syntax.getFunction(functionname.trim());
      //String usage = fun.toString();
     
      //String usage = ((SyntaxDictionaryInterface)syntax).getFunctionUsage(functionname.trim());
           
      //if(usage != null)
      //System.out.println(fst.size());
      if(fst.size() > 0)
      {
        //bit of a hack - there are only a copule functions that have
        //several wasys to call them, so if there are more then one
        //they are sperated by ||s
        //st = new StringTokenizer(usage,"||");
       
        ////////////////////////////////////////////////////////////////
        //TODO figure out why this has to have 2 - it wont show otherwise
        //IContextInformation[] result = new IContextInformation[st.countTokens() + 1];
        //IContextInformation result[] = new IContextInformation[2];
        IContextInformation result[] = new IContextInformation[fst.size()+1];
       
        Iterator i = fst.iterator();
        int x = 0;
        while(i.hasNext())
        {
          //System.out.println(x);
          Function fun = syntax.getFunction((String)i.next());
            //(Function)i.next();

            String usage = fun.toString();
            //System.err.println(usage);
           
            result[x] = new ContextInformation(
              CFPluginImages.get(CFPluginImages.ICON_FUNC),
              //info,
              usage,
              //""
              usage //fun.getHelp()
            );
            this.validator.install(result[x], viewer, documentOffset);
            x++;
        }
        //System.out.println(x);
        result[x] = new ContextInformation(
          "",
          ""
        );
       
        /*int i = 0;
        //while(st.hasMoreTokens())
        //{
          //String info = st.nextToken().trim();
          result[i] = new ContextInformation(
            CFPluginImages.get(CFPluginImages.ICON_FUNC),
            //info,
            usage,
            //""
            usage //fun.getHelp()
          );
          i++;
        //}
        result[i] = new ContextInformation(
          "",
          ""
        ); */
       
        //this.validator.install(result[0], viewer, documentOffset);
       
        return result;
      }
      return null;
     
    }catch(Exception e)
    {
      e.printStackTrace(System.err);
    }
   
    return null;
  }

  /**
   * yeah... not sure what to do here
   */
  public String getErrorMessage() {
    return null;
  }
 
  ///////////////////////////////////////////////////////////////////////////
  protected static class Validator implements IContextInformationValidator,
    IContextInformationPresenter {
   
    //protected int installoffset;
    protected ITextViewer view;
   
    /*
     * @see IContextInformationValidator#isContextInformationValid(int)
     */
    public boolean isContextInformationValid(int offset)
    {
      try
      {
        String paren = view.getDocument().get(
          offset-1,
          1
        );
        //System.out.println(paren);
        if(paren.equals(")"))
        {
          return false;
        }
      }
      catch(Exception e)
      {
        e.printStackTrace(System.err);
        return false;
      }
     
      return true;
      //return Math.abs(installoffset - offset) < 5;
    }

    /*
     * @see IContextInformationValidator#install(IContextInformation, ITextViewer, int)
     */
    public void install(IContextInformation info, ITextViewer viewer, int offset)
    {
      //installoffset = offset;
      view = viewer;
    }

    /*
     * @see org.eclipse.jface.text.contentassist.IContextInformationPresenter#updatePresentation(int, TextPresentation)
     */
    public boolean updatePresentation(int documentPosition, TextPresentation presentation)
    {
      return true;
    }
  };
}
TOP

Related Classes of org.cfeclipse.cfml.editors.CFCompletionProcessor$Validator

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.