x10dt.ui.parser.ParseController.java Source code

Java tutorial

Introduction

Here is the source code for x10dt.ui.parser.ParseController.java

Source

/*******************************************************************************
* Copyright (c) 2008 IBM Corporation.
* 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
*
* Contributors:
*    Robert Fuhrer (rfuhrer@watson.ibm.com) - initial API and implementation
*******************************************************************************/

package x10dt.ui.parser;

import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.io.StringBufferInputStream;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;
import java.util.regex.Pattern;
import java.util.zip.ZipException;
import java.util.zip.ZipFile;

import lpg.runtime.ILexStream;
import lpg.runtime.IPrsStream;
import lpg.runtime.IToken;

import org.antlr.v4.runtime.CommonTokenStream;
import org.antlr.v4.runtime.Token;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IPath;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.NullProgressMonitor;
import org.eclipse.core.runtime.Platform;
import org.eclipse.imp.model.ISourceProject;
import org.eclipse.imp.parser.IMessageHandler;
import org.eclipse.imp.parser.ISourcePositionLocator;
import org.eclipse.imp.services.IAnnotationTypeInfo;
import org.eclipse.imp.services.ILanguageSyntaxProperties;
import org.eclipse.jface.text.IRegion;

import polyglot.ast.Node;
import polyglot.frontend.FileSource;
import polyglot.frontend.Job;
import polyglot.frontend.Source;
import polyglot.frontend.ZipResource;
import polyglot.util.ErrorInfo;
import x10.parser.antlr.ASTBuilder;
import x10.parser.antlr.generated.X10Parser;
import x10.visit.InstanceInvariantChecker;
import x10.visit.PositionInvariantChecker;
import x10dt.core.X10DTCorePlugin;
import x10dt.ui.X10DTUIPlugin;

public class ParseController extends org.eclipse.imp.parser.ParseControllerBase {
    /**
     * A trivial extension of the class ZipResource that permits the user to provide the
     * source text as an explicit parameter, rather than reading it from the .zip file
     * itself. Useful for representing the contents of editor buffers that reside in
     * zip files, even when the editor buffers are read-only.<br>
     * Along with StringSource and StringResource, probably belongs in Polyglot, not here.
     * @author rfuhrer
     */
    public static final class ZipStringResource extends ZipResource {
        private final String contents;

        private ZipStringResource(File source, ZipFile zip, String entryName, String contents) {
            super(source, zip, entryName);
            this.contents = contents;
        }

        @Override
        public InputStream getInputStream() throws IOException {
            return new StringBufferInputStream(contents);
        }
    }

    public interface InvariantViolationHandler {
        public void clear();

        public void handleViolation(ErrorInfo error);

        public void consumeAST(Node root);
    }

    private static final Pattern JAR_IDENTIFIER_PATTERN = Pattern.compile(".*\\.jar:.*");

    private CompilerDelegate fCompiler;
    private IProgressMonitor fMonitor;
    private InvariantViolationHandler fViolationHandler;
    private ISourcePositionLocator fSourcePositionLocator;
    private ISourceProject fProject;
    private IPath fFilePath;
    private IMessageHandler fHandler;
    private CommonTokenStream fTokens;

    public ParseController() {
        super(X10DTCorePlugin.kLanguageName);
    }

    public CommonTokenStream getTokens() {
        return fTokens;
    }

    public CompilerDelegate getCompiler() {
        return fCompiler;
    }

    public ISourcePositionLocator getSourcePositionLocator() {
        if (fSourcePositionLocator == null) {
            fSourcePositionLocator = new PolyglotNodeLocator(fProject);
        }
        return fSourcePositionLocator;
    }

    public ILanguageSyntaxProperties getSyntaxProperties() {
        return new X10SyntaxProperties();
    }

    public void initialize(IPath filePath, ISourceProject project, IMessageHandler handler) {
        fFilePath = filePath;
        fProject = project;
        fHandler = handler;
        fMonitor = new NullProgressMonitor();
    }

    public void setViolationHandler(InvariantViolationHandler handler) {
        fViolationHandler = handler;
    }

    public Object parse(final String contents, IProgressMonitor monitor) {
        Source source = null;

        try {

            int jarPathComponentIdx = findJarIdentifierComponent(fFilePath);

            if (jarPathComponentIdx >= 0) {
                source = buildJarFileEntrySource(contents, jarPathComponentIdx);
            } else {
                String path = fProject != null
                        ? fProject.getRawProject().getLocation().append(fFilePath).toOSString()
                        : fFilePath.toOSString();
                File file = new File(path);

                source = new FileSource(new StringResource(contents, file, path));
            }

            List<Source> streams = Arrays.asList(source);
            IProject proj = (fProject != null) ? fProject.getRawProject() : null;
            IPath sourcePath = (fProject != null)
                    ? Platform.getLocation().append(fProject.getName()).append(fFilePath)
                    : fFilePath;

            fCompiler = new CompilerDelegate(fMonitor, fHandler, proj, sourcePath, fViolationHandler); // Create the compiler
            fCompiler.compile(streams);
        } catch (FileNotFoundException e) {
            // do nothing - presumably the file just got deleted...
        } catch (IOException e) {
            X10DTUIPlugin.log(e);
        } catch (CoreException e) {
            X10DTUIPlugin.log(e);
        } finally {

            // RMF 8/2/2006 - retrieve the AST, token stream and lex stream, if they exist; front-end semantic
            // checks may fail, even though the AST/token-stream are well-formed enough to support various IDE
            // services, like syntax highlighting and the outline view's contents.

            if (source != null && fCompiler != null) {
                final ASTBuilder parser = (ASTBuilder) fCompiler.getParserFor(source);
                fTokens = parser.getTokens();

                //final X10Lexer lexer= fCompiler.getLexerFor(source);
                //fParseStream = parser.getIPrsStream();
                //fLexStream = fParseStream.getILexStream();
                //               fLexStream = lexer.getILexStream();
                //fParser = new ParserDelegate(parser); // HACK - SimpleLPGParseController.cacheKeywordsOnce() needs an IParser and an ILexer, so create them here. Luckily, they're just lightweight wrappers...
                //fLexer = new LexerDelegate(lexer);
                fCurrentAst = fCompiler.getASTFor(source); // getASTFor(fileSource); // TODO use commandLineJobs() instead?

                if (fViolationHandler != null && fCurrentAst != null) {
                    // TODO Tweak appropriate option in Configuration/Options object to include the invariant checking goals
                    Job job = fCompiler.getJobFor(source);
                    PositionInvariantChecker pic = new PositionInvariantChecker(job, "", true);
                    InstanceInvariantChecker iic = new InstanceInvariantChecker(job);

                    ((Node) fCurrentAst).visit(pic);
                    ((Node) fCurrentAst).visit(iic);

                    fViolationHandler.consumeAST((Node) fCurrentAst);
                }
            }
            // RMF 8/2/2006 - cacheKeywordsOnce() must have been run for syntax highlighting to work.
            // Must do this after attempting parsing (even though that might fail), since it depends
            // on the parser/lexer being set in the ExtensionInfo, which only happens as a result of
            // ExtensionInfo.parser(). Ugghh.

            //if (fParser != null) {
            //   cacheKeywordsOnce();
            //}

            fCompiler = null;
            //fParser = null;
            //fLexer = null;
        }
        return fCurrentAst;
    }

    /**
     * Build a Polyglot Source object that refers to the jar file entry given by fFilePath,
     * but using the given String as the source text contents.
     * @param jarPathComponentIdx the index of the path component that contains the "jar:" indicator
     */
    private Source buildJarFileEntrySource(final String contents, int jarPathComponentIdx)
            throws ZipException, IOException {
        Source source;
        String jarPathComponent = fFilePath.segment(jarPathComponentIdx);
        StringBuilder jarPath = new StringBuilder();

        for (int i = 0; i < jarPathComponentIdx; i++) {
            jarPath.append(File.separatorChar);
            jarPath.append(fFilePath.segment(i));
        }

        String jarName = jarPathComponent.substring(0, jarPathComponent.indexOf(':'));
        String trailer = jarPathComponent.substring(jarPathComponent.indexOf(':') + 1);

        jarPath.append(File.separatorChar);
        jarPath.append(jarName);

        StringBuilder entryPath = new StringBuilder();

        entryPath.append(trailer);
        for (int i = jarPathComponentIdx + 1; i < fFilePath.segmentCount(); i++) {
            entryPath.append('/');
            entryPath.append(fFilePath.segment(i));
        }

        File jarFile = new File(jarPath.toString());
        ZipFile zipFile = new ZipFile(jarFile);
        ZipResource zipRsrc = new ZipStringResource(jarFile, zipFile, entryPath.toString(), contents);

        source = new FileSource(zipRsrc);
        return source;
    }

    /**
     * @return the index of the given IPath that indicates that the path refers
     * to a jar file entry, if any, or -1 if this is not a jar file entry path
     */
    private int findJarIdentifierComponent(IPath filePath) {
        int jarPathComponentIdx = -1;
        for (int i = 0; i < filePath.segmentCount(); i++) {
            String seg = filePath.segment(i);
            if (JAR_IDENTIFIER_PATTERN.matcher(seg).matches()) {
                jarPathComponentIdx = i;
                break;
            }
        }
        return jarPathComponentIdx;
    }

    // TODO Use this rather than fCompiler.getASTFor() ? (more reliable?)
    private Object getASTFor(Source source) {
        Collection<Job> cmdJobs = fCompiler.getExtInfo().scheduler().commandLineJobs();
        for (Job job : cmdJobs) {
            if (job.source().equals(source)) {
                return job.ast();
            }
        }
        return null;
    }

    //    public Iterator<IToken> getTokenIterator(IRegion region) {
    //      final int regionOffset= region.getOffset();
    //      final int regionLength= region.getLength();
    //      final int regionEnd= regionOffset + regionLength - 1;
    //
    //      if (fParseStream == null) {
    //         return new Iterator<IToken>() {
    //         public boolean hasNext() {
    //            return false;
    //         }
    //
    //         public IToken next() {
    //            return null;
    //         }
    //
    //         public void remove() {
    //         } };
    //      }
    //      return new Iterator<IToken>() {
    //          final IPrsStream stream= fParseStream;
    //          final int firstTokIdx= getTokenIndexAtCharacter(regionOffset);
    //          final int lastTokIdx;
    //          {
    //              int endIdx= getTokenIndexAtCharacter(regionEnd);
    //              char[] streamChars= stream.getInputChars();
    //              int streamLen= streamChars.length;
    //              try {
    //                  if (regionEnd >= 1 && regionEnd < streamLen
    //                          && streamChars[regionEnd] == IToken.EOF) {
    //                      // skip EOF token (assume LPG puts one at end of input
    //                      // character stream, since it does)
    //                      endIdx--;
    //                  }
    //              } catch (ArrayIndexOutOfBoundsException e) {
    //                  ErrorHandler.logError("ParseController.getTokenIterator(IRegion): error initializing lastTokIdx",
    //                          e);
    //                  // System.err.println("getTokenIterator: new Iterator(..)<init>: ArrayIndexOutOfBoundsException");
    //                  // System.err.println(" regionEnd = " + regionEnd + ", endIdx = " + endIdx + ", streamLen = " + streamLen + ",
    //                  // inputChars.length = " + streamChars.length);
    //              }
    //              lastTokIdx= endIdx;
    //          }
    //          int curTokIdx= Math.max(1, firstTokIdx); // skip bogus initial token
    //
    //          private int getTokenIndexAtCharacter(int offset) {
    //              int result= stream.getTokenIndexAtCharacter(offset);
    //              // getTokenIndexAtCharacter() answers the negative of the index of the
    //              // preceding token if the given offset is not actually within a token.
    //              if (result < 0) {
    //                  result= -result + 1;
    //              }
    //
    //              // The above may leave result set to a value that is one more than the
    //              // last token index, so return the last token index if that's the case
    //              // (This can happen if the end of the file contains some text that
    //              // does not correspond to a token--e.g., if the text represents an adjunct
    //              // or something unrecognized)
    //              if (result >= stream.getTokens().size())
    //                  result= stream.getTokens().size() - 1;
    //
    //              return result;
    //          }
    //
    //          // The following declarations cover the whole input stream, which
    //          // may be a proper superset of the range of the given region.
    //          // For now, that's a simple way to collect the information, and
    //          // most often the given region corresponds to the whole input anyway.
    //          // In any case, iteration is based on the range of the given region.
    //
    //          // The preceding adjuncts for each token
    //          IToken[][] precedingAdjuncts= new IToken[lastTokIdx + 1][];
    //          {
    //              stream.setStreamLength();
    //              for(int i= 0; i < precedingAdjuncts.length; i++) {
    //                  precedingAdjuncts[i]= stream.getPrecedingAdjuncts(i);
    //              }
    //          }
    //
    //          // The current indices for each array of preceding adjuncts
    //          int[] nextPrecedingAdjunct= new int[lastTokIdx + 1];
    //          {
    //              for(int i= 0; i < nextPrecedingAdjunct.length; i++) {
    //                  if (precedingAdjuncts[i].length == 0)
    //                      nextPrecedingAdjunct[i]= -1;
    //                  else
    //                      nextPrecedingAdjunct[i]= 0;
    //              }
    //          }
    //
    //          // The following adjuncts (for the last token only)
    //          IToken[] followingAdjuncts;
    //          {
    //              if (lastTokIdx <= 0)
    //                  followingAdjuncts= new IToken[0];
    //              else
    //                  followingAdjuncts= stream.getFollowingAdjuncts(lastTokIdx);
    //          }
    //
    //          // The current index for the array of following adjuncts
    //          int nextFollowingAdjunct;
    //          {
    //              if (followingAdjuncts.length == 0)
    //                  nextFollowingAdjunct= -1;
    //              else
    //                  nextFollowingAdjunct= 0;
    //          }
    //
    //          // To support hasNext(); initial values may be reset if appropriate
    //          private boolean finalTokenReturned= regionEnd < 1 || lastTokIdx <= 0;
    //          private boolean finalAdjunctsReturned= !(followingAdjuncts.length > 0);
    //
    //          /**
    //           * Tests whether the iterator has any unreturned tokens. These may
    //           * include "regular" tokens and "adjunct" tokens (e.g., representing
    //           * comments).
    //           * 
    //           * @return True if there is another token available, false otherwise
    //           */
    //          public boolean hasNext() {
    //              return !(finalTokenReturned && finalAdjunctsReturned);
    //          }
    //
    //          /**
    //           * Returns the next available token in the iterator (or null if
    //           * there is none)
    //           * 
    //           * Will return a valid token under conditions that would cause
    //           * hasNext() to to return true; conversely, will return null under
    //           * conditions that would cause hasNext() to return false.
    //           * 
    //           * As a side effect, updates the flags that are used to compute the
    //           * value returned by hasNext().
    //           * 
    //           * The returned token may be a "regular" token (which will have a
    //           * corresponding AST node) or an "adjunct" token (which will
    //           * represent a comment). The tokens are returned in the order in
    //           * which they occur in the text, regardless of their kind.
    //           * 
    //           */
    //          public IToken next() {
    //              int next= -1; // for convenience
    //
    //              // If we're not all the way through the tokens
    //              if (curTokIdx <= lastTokIdx) {
    //
    //                  // First check for any remaining preceding adjuncts
    //                  // of the current token
    //                  next= nextPrecedingAdjunct[curTokIdx];
    //                  // If the current token has any unreturned preceding
    //                  // adjuncts
    //                  if (next >= 0 && next < precedingAdjuncts[curTokIdx].length) {
    //                      // Return the next preceding adjunct, incrementing the
    //                      // adjunct index afterwards
    //                      return precedingAdjuncts[curTokIdx][nextPrecedingAdjunct[curTokIdx]++];
    //                  }
    //
    //                  // Flag whether the current token is the last one
    //                  finalTokenReturned= curTokIdx >= lastTokIdx;
    //
    //                  // Return the current token, incrementing the token index
    //                  // afterwards
    //                  return stream.getIToken(curTokIdx++);
    //              }
    //
    //              // If there are any adjuncts following the last token
    //              if (nextFollowingAdjunct >= 0 && nextFollowingAdjunct < followingAdjuncts.length) {
    //                  // Flag whether the current adjunct is the last one
    //                  finalAdjunctsReturned= (nextFollowingAdjunct + 1) >= followingAdjuncts.length;
    //
    //                  // Return the current adjunct, incrementing the adjunct
    //                  // index afterwards
    //                  return followingAdjuncts[nextFollowingAdjunct++];
    //              }
    //
    //              return null;
    //          }
    //
    //          public void remove() {
    //              throw new UnsupportedOperationException("Unimplemented");
    //          }
    //      };
    //    }

    public boolean isKeyword(int type) {
        switch (type) {
        case X10Parser.ABSTRACT:
        case X10Parser.AS:
        case X10Parser.ASSERT:
        case X10Parser.ASYNC:
        case X10Parser.AT:
        case X10Parser.ATEACH:
        case X10Parser.ATHOME:
        case X10Parser.ATOMIC:
        case X10Parser.BREAK:
        case X10Parser.CASE:
        case X10Parser.CATCH:
        case X10Parser.CLASS:
        case X10Parser.CLOCKED:
        case X10Parser.CONTINUE:
        case X10Parser.DEF:
        case X10Parser.DEFAULT:
        case X10Parser.DO:
        case X10Parser.ELSE:
        case X10Parser.EXTENDS:
        case X10Parser.FALSE:
        case X10Parser.FINAL:
        case X10Parser.FINALLY:
        case X10Parser.FINISH:
        case X10Parser.FOR:
        case X10Parser.GOTO:
        case X10Parser.HASZERO:
        case X10Parser.HERE:
        case X10Parser.IF:
        case X10Parser.IMPLEMENTS:
        case X10Parser.IMPORT:
        case X10Parser.IN:
        case X10Parser.INSTANCEOF:
        case X10Parser.INTERFACE:
        case X10Parser.ISREF:
        case X10Parser.NATIVE:
        case X10Parser.NEW:
        case X10Parser.NULL:
        case X10Parser.OFFER:
        case X10Parser.OFFERS:
        case X10Parser.OPERATOR:
        case X10Parser.PACKAGE:
        case X10Parser.PRIVATE:
        case X10Parser.PROPERTY:
        case X10Parser.PROTECTED:
        case X10Parser.PUBLIC:
        case X10Parser.RETURN:
        case X10Parser.SELF:
        case X10Parser.STATIC:
        case X10Parser.STRUCT:
        case X10Parser.SUPER:
        case X10Parser.SWITCH:
        case X10Parser.THIS:
        case X10Parser.THROW:
        case X10Parser.THROWS:
        case X10Parser.TRANSIENT:
        case X10Parser.TRUE:
        case X10Parser.TRY:
        case X10Parser.TYPE:
        case X10Parser.VAL:
        case X10Parser.VAR:
        case X10Parser.VOID:
        case X10Parser.WHEN:
        case X10Parser.WHILE:
            return true;
        default:
            return false;
        }
    }

    public IAnnotationTypeInfo getAnnotationTypeInfo() {
        // TODO Auto-generated method stub
        return null;
    }

    public Iterator getTokenIterator(IRegion region) {
        final int regionOffset = region.getOffset();
        final int regionLength = region.getLength();
        final int regionEnd = regionOffset + regionLength - 1;

        List<Token> tokens = fTokens == null ? new ArrayList<Token>() : fTokens.get(regionOffset, regionEnd);
        List<IToken> iTokens = new ArrayList<IToken>();
        if (tokens != null) {
            for (Token t : tokens) {
                iTokens.add(getIToken(t));
            }
        }
        return iTokens.listIterator();

    }

    public IPrsStream getParseStream() {
        return null;
    }

    public ILexStream getLexStream() {
        return null;
    }

    private IToken getIToken(final Token token) {
        IToken ret = new IToken() {

            public int getAdjunctIndex() {
                // TODO Auto-generated method stub
                return 0;
            }

            public int getColumn() {
                return token.getCharPositionInLine();
            }

            public int getEndColumn() {
                return token.getCharPositionInLine() + token.getStopIndex() - token.getStartIndex();
            }

            public int getEndLine() {
                return token.getLine();
            }

            public int getEndOffset() {
                return token.getStopIndex();
            }

            public IToken[] getFollowingAdjuncts() {
                // TODO Auto-generated method stub
                return null;
            }

            public ILexStream getILexStream() {
                // TODO Auto-generated method stub
                return null;
            }

            public IPrsStream getIPrsStream() {
                // TODO Auto-generated method stub
                return null;
            }

            public int getKind() {
                return token.getType();
            }

            public ILexStream getLexStream() {
                // TODO Auto-generated method stub
                return null;
            }

            public int getLine() {
                return token.getLine();
            }

            public IToken[] getPrecedingAdjuncts() {
                // TODO Auto-generated method stub
                return null;
            }

            public IPrsStream getPrsStream() {
                // TODO Auto-generated method stub
                return null;
            }

            public int getStartOffset() {
                return token.getStartIndex();
            }

            public int getTokenIndex() {
                return token.getTokenIndex();
            }

            public String getValue(char[] arg0) {
                return token.getText();
            }

            public void setAdjunctIndex(int arg0) {
                // TODO Auto-generated method stub

            }

            public void setEndOffset(int arg0) {
            }

            public void setKind(int arg0) {
                // TODO Auto-generated method stub

            }

            public void setStartOffset(int arg0) {
                // TODO Auto-generated method stub

            }

            public void setTokenIndex(int arg0) {
                // TODO Auto-generated method stub

            }

        };
        return ret;
    }
}