Package org.objectstyle.wolips.wodclipse.editor

Source Code of org.objectstyle.wolips.wodclipse.editor.WodCompletionProcessor

/*
* ====================================================================
*
* The ObjectStyle Group Software License, Version 1.0
*
* Copyright (c) 2005 The ObjectStyle Group and individual authors of the
* software. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met: 1.
* Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer. 2. Redistributions in
* binary form must reproduce the above copyright notice, this list of
* conditions and the following disclaimer in the documentation and/or other
* materials provided with the distribution. 3. The end-user documentation
* included with the redistribution, if any, must include the following
* acknowlegement: "This product includes software developed by the ObjectStyle
* Group (http://objectstyle.org/)." Alternately, this acknowlegement may
* appear in the software itself, if and wherever such third-party
* acknowlegements normally appear. 4. The names "ObjectStyle Group" and
* "Cayenne" must not be used to endorse or promote products derived from this
* software without prior written permission. For written permission, please
* contact andrus@objectstyle.org. 5. Products derived from this software may
* not be called "ObjectStyle" nor may "ObjectStyle" appear in their names
* without prior written permission of the ObjectStyle Group.
*
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES,
* INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
* FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
* OBJECTSTYLE GROUP OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
* OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
* EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
* ====================================================================
*
* This software consists of voluntary contributions made by many individuals
* on behalf of the ObjectStyle Group. For more information on the ObjectStyle
* Group, please see <http://objectstyle.org/> .
*/
package org.objectstyle.wolips.wodclipse.editor;

import java.util.Set;
import java.util.TreeSet;

import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.resources.ResourcesPlugin;
import org.eclipse.core.runtime.IPath;
import org.eclipse.jdt.core.IJavaProject;
import org.eclipse.jdt.core.IType;
import org.eclipse.jdt.core.JavaCore;
import org.eclipse.jdt.core.JavaModelException;
import org.eclipse.jface.text.BadLocationException;
import org.eclipse.jface.text.IDocument;
import org.eclipse.jface.text.IRegion;
import org.eclipse.jface.text.ITextViewer;
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.IContextInformationValidator;
import org.eclipse.jface.text.rules.IRule;
import org.eclipse.jface.text.rules.WhitespaceRule;
import org.eclipse.ui.IEditorInput;
import org.eclipse.ui.IPathEditorInput;
import org.eclipse.ui.part.FileEditorInput;
import org.objectstyle.wolips.bindings.api.ApiUtils;
import org.objectstyle.wolips.bindings.utils.BindingReflectionUtils;
import org.objectstyle.wolips.bindings.wod.TypeCache;
import org.objectstyle.wolips.wodclipse.WodclipsePlugin;
import org.objectstyle.wolips.wodclipse.core.completion.WodCompletionProposal;
import org.objectstyle.wolips.wodclipse.core.completion.WodCompletionUtils;
import org.objectstyle.wolips.wodclipse.core.completion.WodParserCache;
import org.objectstyle.wolips.wodclipse.core.parser.AssignmentOperatorWordDetector;
import org.objectstyle.wolips.wodclipse.core.parser.BindingNameRule;
import org.objectstyle.wolips.wodclipse.core.parser.BindingValueRule;
import org.objectstyle.wolips.wodclipse.core.parser.CloseDefinitionWordDetector;
import org.objectstyle.wolips.wodclipse.core.parser.ElementNameRule;
import org.objectstyle.wolips.wodclipse.core.parser.ElementTypeOperatorWordDetector;
import org.objectstyle.wolips.wodclipse.core.parser.ElementTypeRule;
import org.objectstyle.wolips.wodclipse.core.parser.EndAssignmentWordDetector;
import org.objectstyle.wolips.wodclipse.core.parser.OpenDefinitionWordDetector;
import org.objectstyle.wolips.wodclipse.core.parser.OperatorRule;
import org.objectstyle.wolips.wodclipse.core.parser.RulePosition;
import org.objectstyle.wolips.wodclipse.core.parser.WodScanner;
import org.objectstyle.wolips.wodclipse.core.preferences.PreferenceConstants;

/**
* @author mike
*/
public class WodCompletionProcessor implements IContentAssistProcessor {
  private WodEditor _editor;

  public WodCompletionProcessor(WodEditor editor) {
    _editor = editor;
  }

  public char[] getContextInformationAutoActivationCharacters() {
    return null;
  }

  public IContextInformationValidator getContextInformationValidator() {
    return null;
  }

  public IContextInformation[] computeContextInformation(ITextViewer viewer, int offset) {
    return null;
  }

  public char[] getCompletionProposalAutoActivationCharacters() {
    return new char[] { ':', '.', '=' };
  }

  public ICompletionProposal[] computeCompletionProposals(ITextViewer viewer, int _offset) {
    Set<WodCompletionProposal> completionProposalsSet = new TreeSet<WodCompletionProposal>();
    try {
      int offset = _offset;
      TypeCache typeCache = WodParserCache.getTypeCache();
      IDocument document = viewer.getDocument();
      IEditorInput input = _editor.getEditorInput();
      if (input instanceof IPathEditorInput) {
        IPathEditorInput pathInput = (IPathEditorInput) input;
        IPath path = pathInput.getPath();
        IFile wodFile = ResourcesPlugin.getWorkspace().getRoot().getFileForLocation(path);
        IProject project = wodFile.getProject();
        IJavaProject javaProject = JavaCore.create(project);

        // Without an underlying model, we have to rescan the line to
        // figure out exactly
        // what the current matching rule was, so we know what kind of
        // token we're dealing with.
        IRegion lineRegion = document.getLineInformationOfOffset(offset);
        WodScanner scanner = WodScanner.newWODScanner();
        scanner.setRange(document, lineRegion.getOffset(), lineRegion.getLength());
        boolean foundToken = false;
        RulePosition rulePosition = null;
        while (!foundToken && (rulePosition = scanner.nextRulePosition()) != null) {
          int tokenOffset = rulePosition.getTokenOffset();
          if (offset == lineRegion.getOffset() && offset == tokenOffset) {
            foundToken = true;
          } else if (offset > tokenOffset && offset <= rulePosition.getTokenEndOffset()) {
            foundToken = true;
          }
        }

        // We can't reliably use rulePosition here because it might be
        // null ...
        int tokenOffset = scanner.getTokenOffset();
        int tokenLength = scanner.getTokenLength();
        IRule rule = (rulePosition == null) ? null : rulePosition.getRule();
        // If you make a completion request in the middle of whitespace,
        // we don't want to select the whitespace, so zero out the
        // whitespace token offsets.
        if (rule instanceof WhitespaceRule) {
          int partialOffset = (offset - tokenOffset);
          offset += partialOffset;
          tokenOffset += partialOffset;
          tokenLength = 0;
        } else {
          viewer.setSelectedRange(offset, tokenLength - (offset - tokenOffset));
        }
        String token = document.get(tokenOffset, tokenLength);
        String tokenType = null;
        if (foundToken && rulePosition != null) {
          if (rulePosition.isRuleOfType(ElementNameRule.class)) {
            tokenType = PreferenceConstants.ELEMENT_NAME;
          } else if (rulePosition.isRuleOfType(ElementTypeRule.class)) {
            tokenType = PreferenceConstants.ELEMENT_TYPE;
          } else if (rulePosition.isRuleOfType(BindingNameRule.class)) {
            tokenType = PreferenceConstants.BINDING_NAME;
          } else if (rulePosition.isRuleOfType(BindingValueRule.class)) {
            tokenType = PreferenceConstants.BINDING_VALUE;
          } else if (rulePosition.isRuleOfType(OperatorRule.class)) {
            tokenOffset += tokenLength;
            tokenLength = 0;
            if (RulePosition.isOperatorOfType(rulePosition, CloseDefinitionWordDetector.class)) {
              tokenType = PreferenceConstants.ELEMENT_NAME;
            } else if (RulePosition.isOperatorOfType(rulePosition, ElementTypeOperatorWordDetector.class)) {
              tokenType = PreferenceConstants.ELEMENT_TYPE;
            } else if (RulePosition.isOperatorOfType(rulePosition, OpenDefinitionWordDetector.class) || RulePosition.isOperatorOfType(rulePosition, EndAssignmentWordDetector.class)) {
              tokenType = PreferenceConstants.BINDING_NAME;
            } else if (RulePosition.isOperatorOfType(rulePosition, AssignmentOperatorWordDetector.class)) {
              tokenType = PreferenceConstants.BINDING_VALUE;
            }
          }
        }

        boolean guessed = false;
        // If there was no matching token type, then that means we're
        // in an invalid parse state and we have to "guess". So we
        // backscan
        // until we find an operator that we know about, which will tell
        // us
        // where we are in the document.
        if (tokenType == null) {
          int startOffset = tokenOffset;
          if (startOffset != 0 && startOffset == document.getLength()) {
            startOffset--;
          }
          // int hintChar = -1;
          // for (int startOffset = tokenOffset - 1; tokenType == null
          // && startOffset > 0; startOffset--) {
          boolean tentativeElementType = false;
          for (; tokenType == null && startOffset > 0; startOffset--) {
            int ch = document.getChar(startOffset);
            if (ch == ':') {
              tentativeElementType = true;
              //tokenType = PreferenceConstants.ELEMENT_TYPE;
              //guessed = true;
            } else if (ch == '{' || ch == ';') {
              tokenType = PreferenceConstants.BINDING_NAME;
              guessed = true;
            } else if (ch == '=') {
              tokenType = PreferenceConstants.BINDING_VALUE;
              guessed = true;
            } else if (ch == '}') {
              // just being explicit
              tokenType = PreferenceConstants.ELEMENT_NAME;
              guessed = true;
            }
          }
          if (tentativeElementType && tokenType == PreferenceConstants.BINDING_VALUE) {
            tokenType = PreferenceConstants.BINDING_VALUE_NAMESPACE;
            guessed = true;
          }
          else if (tentativeElementType) {
            tokenType = PreferenceConstants.ELEMENT_TYPE;
            guessed = true;
          }
        }

        if (tokenType == null) {
          tokenType = PreferenceConstants.ELEMENT_NAME;
          guessed = true;
        }

        // ... Fill in completion proposals based on the token type
        if (tokenType == PreferenceConstants.ELEMENT_NAME) {
          // We really need something like the AST ... This is a
          // pretty expensive
          // way to go here. To find element names that have already
          // been mapped, we
          // reparse the wod file. Lame.
          Set<String> alreadyUsedElementNames = WodScanner.getTextForRulesOfType(document, ElementNameRule.class);
          WodParserCache wodParserCache = WodParserCache.parser(((FileEditorInput) _editor.getEditorInput()).getFile());
          WodCompletionUtils.fillInElementNameCompletionProposals(alreadyUsedElementNames, token, tokenOffset, offset, completionProposalsSet, guessed, wodParserCache.getHtmlEntry().getHtmlElementCache());
        } else if (tokenType == PreferenceConstants.ELEMENT_TYPE) {
          WodCompletionUtils.fillInElementTypeCompletionProposals(javaProject, token, tokenOffset, offset, completionProposalsSet, guessed, null);
        } else if (tokenType == PreferenceConstants.BINDING_NAME) {
          IType elementType = findNearestElementType(javaProject, document, scanner, tokenOffset, typeCache);
          WodCompletionUtils.fillInBindingNameCompletionProposals(javaProject, elementType, token, tokenOffset, offset, completionProposalsSet, guessed, typeCache);
        } else if (tokenType == PreferenceConstants.BINDING_VALUE) {
          // MS: We've probably already found the type for our current component. Just get the parser cache entry
          // and pull the type off of it.
          IType elementType = _editor.getComponentsLocateResults().getDotJavaType();
          boolean checkBindingValue = false;
          if (elementType != null) {
            checkBindingValue = WodCompletionUtils.fillInBindingValueCompletionProposals(javaProject, elementType, token, tokenOffset, offset, completionProposalsSet, typeCache);
          }
          if (checkBindingValue) {
            try {
              // We might (probably do) have a syntactically
              // invalid wod file at this point, so we need to
              // hunt for the name of the binding that this value
              // corresponds to ...
              int equalsIndex = WodCompletionProcessor.scanBackFor(document, offset, new char[] { '=' }, false);
              int noSpaceIndex = WodCompletionProcessor.scanBackFor(document, equalsIndex - 1, new char[] { ' ', '\t', '\n', '\r' }, true);
              int spaceIndex = WodCompletionProcessor.scanBackFor(document, noSpaceIndex, new char[] { ' ', '\t', '\n', '\r' }, false);
              String bindingName = document.get(spaceIndex + 1, noSpaceIndex - spaceIndex);
              elementType = findNearestElementType(javaProject, document, scanner, offset, typeCache);
              String[] validValues = ApiUtils.getValidValues(token, javaProject, _editor.getComponentsLocateResults().getDotJavaType(), elementType, bindingName, typeCache);
              if (validValues != null) {
                String partialToken = WodCompletionUtils.partialToken(token, tokenOffset, offset);
                String lowercasePartialToken = partialToken.toLowerCase();
                for (int i = 0; i < validValues.length; i++) {
                  if (validValues[i].toLowerCase().startsWith(lowercasePartialToken)) {
                    completionProposalsSet.add(new WodCompletionProposal(token, tokenOffset, offset, validValues[i]));
                  }
                }
              }
            } catch (Throwable t) {
              t.printStackTrace();
              WodclipsePlugin.getDefault().log(t);
            }
          }
        }
      }
    } catch (Exception e) {
      e.printStackTrace();
      WodclipsePlugin.getDefault().log(e);
    }

    return completionProposalsSet.toArray(new WodCompletionProposal[completionProposalsSet.size()]);
  }

  public String getErrorMessage() {
    return null;
  }

  protected IType findNearestElementType(IJavaProject _project, IDocument _document, WodScanner _scanner, int _offset, TypeCache cache) throws BadLocationException, JavaModelException {
    // Go hunting for the element type in a potentially malformed document
    // ...
    IType type = null;
    int offset = _offset;
    while (offset != -1 && type == null) {
      offset = WodCompletionProcessor.scanBackFor(_document, offset, new char[] { '{' }, false, true);
      int colonOffset = WodCompletionProcessor.scanBackFor(_document, offset, new char[] { ':' }, false, true);
      if (colonOffset != -1) {
        _scanner.setRange(_document, colonOffset, _offset - colonOffset);
        RulePosition elementRulePosition = _scanner.getFirstRulePositionOfType(ElementTypeRule.class);
        if (elementRulePosition != null) {
          String elementTypeName = elementRulePosition.getText();
          type = BindingReflectionUtils.findElementType(_project, elementTypeName, false, cache);
          offset = -1;
        } else {
          // we didn't find a ElementTypeRule
          offset--;
        }
      } else {
        // failed colonoscopy
        type = null;
      }
    }
    return type;
  }

  protected static int scanBackFor(IDocument _document, int _offset, char[] _lookForChars, boolean _negate) throws BadLocationException {
    return scanBackFor(_document, _offset, _lookForChars, _negate, false);
  }
 
  protected static int scanBackFor(IDocument _document, int _offset, char[] _lookForChars, boolean _negate, boolean excludeQuoted) throws BadLocationException {
    int offset = _offset;
    if (offset >= _document.getLength()) {
      offset--;
    }
    int foundIndex = -1;
    boolean inQuote = false;
    char quoteChar = 0;
    for (int i = offset; foundIndex == -1 && i >= 0; i--) {
      char ch = _document.getChar(i);
      if (ch == '\'' || ch == '"') {
        boolean escaped = i > 0 && _document.getChar(i-1) == '\\';
        if (inQuote && quoteChar == ch && !escaped) {
          inQuote = false;
          quoteChar = 0;
        }
        else if (!inQuote){
          inQuote = true;
          quoteChar = ch;
        }
      }
      for (int lookForCharNum = 0; foundIndex == -1 && lookForCharNum < _lookForChars.length; lookForCharNum++) {
        if (ch == _lookForChars[lookForCharNum] && !(inQuote && excludeQuoted)) {
          foundIndex = i;
        }
      }
      if (_negate) {
        if (foundIndex != -1) {
          foundIndex = -1;
        } else {
          foundIndex = i;
        }
      }
    }
    return foundIndex;
  }
}
TOP

Related Classes of org.objectstyle.wolips.wodclipse.editor.WodCompletionProcessor

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.