Package org.epic.perleditor.editors.util

Source Code of org.epic.perleditor.editors.util.PerlValidatorBase$ParsedErrorLine

package org.epic.perleditor.editors.util;

import java.io.*;
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import org.eclipse.core.resources.*;
import org.eclipse.core.runtime.*;
import org.eclipse.jface.text.BadLocationException;
import org.eclipse.jface.text.Document;
import org.epic.core.Constants;
import org.epic.core.PerlCore;
import org.epic.core.util.PerlExecutor;
import org.epic.perleditor.PerlEditorPlugin;

/**
* Abstract base class for PerlValidator.
*
* This class contains those parts of PerlValidator implementation
* which are independent of the Eclipse runtime environment to aid
* testing.
*
* @author jploski
*/
abstract class PerlValidatorBase
{
    private static final boolean DEBUG = true;
  private static int maxErrorsShown = 500;
    private static final int BUF_SIZE = 1024;
   
    private final ILog log;
  private final PerlValidatorErrors errors;
    private final PerlExecutor executor;
 
    protected PerlValidatorBase(ILog log, PerlExecutor executor)
    {
        this.log = log;
        this.executor = executor;
       
        errors = new PerlValidatorErrors();
    }
   
    /**
     * Validates the provided source code, creates markers on the given
     * IResource.
     */
  public synchronized void validate(IResource resource, String sourceCode)
        throws CoreException
    {
    //TODO check if perlOutput is empty (indicates error)          

    // Mark problem markers as unused
        //
        // TODO: Scheduling all markers for removal at this point is actually
        // wrong because some markers might have been added by validation of
        // another resource (trigger). They should only disappear if validation
        // of all triggers no longer signals a problem. However, for the time
        // being we can live with disappearing markers - they typically do not
        // vanish completely because a related marker is still left in
        // the trigger as a reminder (at least this is the case for the most
        // common 'Can't locate .. in @INC' error message, which always leaves
        // "Compilation failed in require" in the trigger).
        //
        clearAllUsedMarkers(resource);
       
        if (isIgnoredPath(resource)) return;

        String perlOutput = runPerl(resource, sourceCode);

        if (DEBUG) printPerlOutput(perlOutput);
       
        List lines = makeLinesList(perlOutput);
        boolean continued = false;

    // Markers have to be added in reverse order
    // Otherwise lower line number will appear at the end of the list
    for (int i = lines.size() - 1; i >= 0; i--)
        {               
            String line = (String) lines.get(i);
           
            // Is this a continuation of the line i-1?
            if (line.startsWith(" "))
            {
                continued = true;
                continue;
            }
            else
            {
                if (continued) line += lines.get(i + 1);
                continued = false;               
            }
           
            ParsedErrorLine pline = new ParsedErrorLine(line, log);           
            IResource errorResource = getErrorResource(pline, resource);

            if (shouldIgnore(pline, errorResource)) continue;

            PerlValidatorErrors.ErrorMessage errorMsg =
                errors.getErrorMessage(pline.getMessage());
           
            Integer lineNr = new Integer(pline.getLineNumber());
            Map attributes = new HashMap(11);

            attributes.put(IMarker.SEVERITY, errorMsg.getSeverity());
      attributes.put(
                Constants.MARKER_ATTR_PERL_ERROR_EXPLANATION,
                errorMsg.getExplanation());           

            if (!pline.isLocalError() && errorResource == resource)
            {
                // last resort: we have a non-local error, but the resource
                // referred to in the error message could not be found
                attributes.put(
                    IMarker.MESSAGE,
                    pline.getMessage() + " in " +
                    pline.getPath() + " line " + lineNr);
            }
            else
            {
                attributes.put(IMarker.MESSAGE, pline.getMessage());
                attributes.put(IMarker.LINE_NUMBER, lineNr);
               
                if (shouldUnderlineError(errorResource, pline.getLineNumber()))
                {
                    String errorSourceCode;
                   
                    try
                    {                   
                        if (errorResource == resource) errorSourceCode = sourceCode;
                        else errorSourceCode = readSourceFile(errorResource);
                   
                        underlineError(
                            errorResource,
                            errorSourceCode,
                            pline.getLineNumber(),
                            attributes);
                    }
                    catch (IOException e)
                    {
                        Status status = new Status(
                            Status.ERROR,
                            PerlEditorPlugin.getPluginId(),
                            IStatus.OK,
                            "Could not read source file of resource: " +
                            errorResource.getLocation() + ". Error markers will " +
                            "be incorrect for this resource.",
                            e);
                        // trouble reading the other file's source code
                        throw new CoreException(status);
                    }
                }
            }
           
            addMarker(errorResource, attributes);
    }
       
        removeUnusedMarkers(resource);
  }
   
    protected abstract void addMarker(IResource resource, Map attributes);

    protected abstract void clearAllUsedMarkers(IResource resource);

    protected IResource getErrorResource(ParsedErrorLine line, IResource resource)
    {
        return line.isLocalError() ? resource : null;
    }
   
    protected List getPerlArgs()
    {
        List args = new ArrayList();
        args.add("-c");
        return args;
    }
   
    protected abstract boolean isProblemMarkerPresent(
        ParsedErrorLine line, IResource resource);

    /**
     * Reads contents of the given text file.
     *
     * @param path  file that should be read
     * @return text contents
     */
    protected String readSourceFile(String path, String charset) throws IOException
    {
        BufferedReader in = null;
       
        try
        {       
            StringWriter sourceCode = new StringWriter();

            char[] buf = new char[BUF_SIZE];
            try
            {
                in = new BufferedReader(charset != null
                    ? new InputStreamReader(new FileInputStream(path), charset)
                    : new InputStreamReader(new FileInputStream(path)));
            }
            catch (UnsupportedEncodingException e)
            {
                in = new BufferedReader(new InputStreamReader(new FileInputStream(path)));
            }

            int read = 0;
            while ((read = in.read(buf)) > 0) sourceCode.write(buf, 0, read);

            return sourceCode.toString();
        }
        finally
        {
            if (in != null) try { in.close(); } catch (IOException e) { }
        }
    }
   
   
    /**
     * Reads contents of the given text file.
     *
     * @param resource file that should be read
     * @return text contents
     */
    protected String readSourceFile(IResource resource) throws IOException
    {
        String charset;
       
        try { charset = ((IFile) resource).getCharset(); }
        catch (CoreException e) { charset = null; }
       
        return readSourceFile(resource.getLocation().makeAbsolute().toString(), charset);
    }
   
    protected abstract void removeUnusedMarkers(IResource resource);
   
    protected abstract boolean shouldUnderlineError(IResource resource, int lineNr);
   
    /**
     * Splits up the given text content into a list of up to maxErrorsShown lines.
     * If there are more lines in content, remaining lines are ignored.
     *
     * @return a list of Strings, one per line (without line terminators)
     */
    private static List makeLinesList(String perlOutput)
    {
        List lines = new ArrayList();
        StringTokenizer st = new StringTokenizer(perlOutput, "\r\n");
        int lineCount = 0;

        while (st.hasMoreTokens() && lineCount < maxErrorsShown)
        {
            lines.add(st.nextToken());
            lineCount++;
        }
        return lines;
    }

    /**
     * @return true if path of resource matches one of the "ignored path" patterns
     *         configured in project properties that should be excluded from validation;
     *         false otherwise
     */
    private boolean isIgnoredPath(IResource resource)
    {
        List ignoredPaths = PerlCore.create(resource.getProject()).getIgnoredPaths();
        if (!ignoredPaths.isEmpty())
        {
            for (Iterator i = ignoredPaths.iterator(); i.hasNext();)
            {
                Pattern p = (Pattern) i.next();
                String path = resource.getProjectRelativePath().toString();              
                if (p.matcher(path).matches())
                {
                    removeUnusedMarkers(resource);
                    return true;
                }
            }
        }
        return false;
    }
   
    private void printPerlOutput(String perlOutput)
    {
        if (perlOutput.indexOf("syntax OK") == -1)
        {
      System.out.println("-----------------------------------------");
      System.out.println("           OUTPUT");
      System.out.println("-----------------------------------------");
      System.out.println(perlOutput);
      System.out.println("-----------------------------------------");
        }
    }
   
    /**
     * @return true if the error message contained in the line should
     *         be ignored, false otherwise
     */
    private boolean shouldIgnore(ParsedErrorLine line, IResource resource)
    {      
        // No line number?
        if (line.getLineNumber() < 0) return true;
       
        // Marker already present?       
        if (isProblemMarkerPresent(line, resource))
        {
            return true;
        }
       
        // Useless error message, with better ones around in the same output?
        if (line.getMessage().indexOf("BEGIN failed--compilation aborted") == 0)
            return true;
        else
            return false;
    }

    /**
     * Executes the Perl interpreter to validate the given script.
     *
     * @param resource    source file
     * @param sourceCode  text of the source file
     * @return stderr output of the Perl interpreter
     */
    private String runPerl(IResource resource, String sourceCode)
        throws CoreException
    {
        return executor.execute(resource, getPerlArgs(), sourceCode).stderr;
    }      
   
    private void underlineError(
        IResource resource, String sourceCode, int lineNo, Map attributes)
    {
        // Get start and end offset
        int lineOffset = 0;
        try
        {
            Document document = new Document(sourceCode);
            lineOffset = document.getLineOffset(lineNo - 1);
        }
        catch (BadLocationException e)
        {
            // this one should never occur
            log.log(
                new Status(Status.ERROR,
                    PerlEditorPlugin.getPluginId(),
                    IStatus.OK,
                    "Unexpected exception in PerlValidator.underlineError: " +
                    resource.getFullPath() + ", lineNo: " + lineNo +
                    "; report it as bug in plug-in " +
                    PerlEditorPlugin.getPluginId(),
                    e));
        }

        int endOfLine = sourceCode.indexOf("\n", lineOffset);
        String markerLine;

        if (endOfLine != -1) {
            markerLine =
                sourceCode.substring(lineOffset, endOfLine);
        } else {
            markerLine = sourceCode.substring(lineOffset);
        }

        char[] bytes = markerLine.toCharArray();

        int start = 0;
        while (start < bytes.length) {
            if (bytes[start] != '\t' && bytes[start] != ' ') {
                break;
            }
            start++;
        }

        start += lineOffset;

        int end = start + markerLine.trim().length();

        attributes.put(IMarker.CHAR_START, new Integer(start));
        attributes.put(IMarker.CHAR_END, new Integer(end));
    }
   
    protected static class ParsedErrorLine
    {
        private static final Pattern errorLineNoPattern =
            Pattern.compile("^(.*) at (\\S+) line (\\d+)[\\.,]");

        private static final Pattern cgiCarpPattern =
            Pattern.compile("^\\[.*?\\] \\S+: (.*)");
       
        private final ILog log;
        private final String line;
        private final String msg;
        private final String path;
        private final int lineNo;
       
        public ParsedErrorLine(String line, ILog log)
        {
            this.line = line;
            this.log =log;
           
            Matcher m = errorLineNoPattern.matcher(line);
            if (m.find())
            {
                msg = normalizeMsg(m.group(1));
                path = m.group(2);
                lineNo = parseInt(m.group(3));             
            }
            else
            {
                msg = normalizeMsg(line);
                path = "-";
                lineNo = -1;
            }
        }       
       
        public int getLineNumber()
        {
            return lineNo;
        }
       
        public String getMessage()
        {
            return msg;
        }
       
        public String getPath()
        {
            return path;
        }
       
        public boolean isLocalError()
        {
            return "-".equals(path);
        }
       
        public String toString()
        {
            return msg + ", " + path + ":" + lineNo;
        }
       
        private int parseInt(String str)
        {
            try { return Integer.parseInt(str); }
            catch (NumberFormatException e)
            {
                // this one should never occur
                log.log(
                    new Status(Status.ERROR,
                        PerlEditorPlugin.getPluginId(),
                        IStatus.OK,
                        "Could not parse line number contained in Perl " +
                        "error message {" + line + "}; report it as a bug " +
                        "in plug-in " + PerlEditorPlugin.getPluginId(),
                        e));
                return -1;
            }
        }
       
        private String normalizeMsg(String msg)
        {
            return stripCGICarpOutput(msg);
        }
       
        /**
         * @return msg with CGI::Carp's timestamp stripped from the beginning
         *         (if it was present)
         */
        private String stripCGICarpOutput(String msg)
        {
            if (msg.startsWith("["))
            {
                Matcher m = cgiCarpPattern.matcher(msg);
                if (m.find()) return m.group(1);
            }
            return msg;
        }
    }
}
TOP

Related Classes of org.epic.perleditor.editors.util.PerlValidatorBase$ParsedErrorLine

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.