/*******************************************************************************
* Copyright (c) 2008, 2017 xored software, Inc. and others.
*
* 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:
* xored software, Inc. - initial API and Implementation (Alex Panchenko)
*******************************************************************************/
package org.eclipse.dltk.tcl.internal.tclchecker.v5;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.Path;
import org.eclipse.dltk.core.CorrectionEngine;
import org.eclipse.dltk.core.ISourceModule;
import org.eclipse.dltk.core.builder.ISourceLineTracker;
import org.eclipse.dltk.tcl.internal.tclchecker.AbstractOutputProcessor;
import org.eclipse.dltk.tcl.internal.tclchecker.Coord;
import org.eclipse.dltk.tcl.internal.tclchecker.CoordRange;
import org.eclipse.dltk.tcl.internal.tclchecker.ILineTrackerFactory;
import org.eclipse.dltk.tcl.internal.tclchecker.ITclCheckerReporter;
import org.eclipse.dltk.tcl.internal.tclchecker.Messages;
import org.eclipse.dltk.tcl.internal.tclchecker.TclCheckerMarker;
import org.eclipse.dltk.tcl.internal.tclchecker.TclCheckerProblem;
import org.eclipse.dltk.validators.core.IValidatorOutput;
import org.eclipse.osgi.util.NLS;
public class Checker5OutputProcessor extends AbstractOutputProcessor {
private final IValidatorOutput console;
private final ITclCheckerReporter reporter;
private final ILineTrackerFactory lineTrackerFactory;
public Checker5OutputProcessor(IProgressMonitor monitor, IValidatorOutput console, ITclCheckerReporter reporter,
ILineTrackerFactory lineTrackerFactory) {
super(monitor);
this.console = console;
this.reporter = reporter;
this.lineTrackerFactory = lineTrackerFactory;
}
@Override
public void processErrorLine(String line) {
console.println(line);
}
private final StringBuilder buffer = new StringBuilder();
private int scanned = 0;
private int checked = 0;
@Override
public void processLine(String line) throws CoreException {
console.println(line);
buffer.append(line);
buffer.append("\n"); //$NON-NLS-1$
final List<IToken> tokens = new TclDictionaryParser().parseDictionary(buffer.toString());
if (tokens != null) {
buffer.setLength(0);
if (tokens.size() >= 2 && !tokens.get(0).hasChildren()) {
if (CMD_MESSAGE.equals(tokens.get(0).getText()) && tokens.get(1).hasChildren()) {
final Map<String, IToken> attributes = parseAttributes(tokens.get(1).getChildren());
if (attributes.containsKey(ATTR_FILE) && attributes.containsKey(ATTR_LINE)
&& attributes.containsKey(ATTR_MESSAGE_ID) && attributes.containsKey(ATTR_MESSAGE_TEXT)) {
processMessage(attributes);
}
} else if (tokens.size() == 3 && CMD_PROGRESS.equals(tokens.get(0).getText())) {
final String progressSubCmd = tokens.get(1).getText();
if (PROGRESS_SCANNING.equals(progressSubCmd)) {
subTask(NLS.bind(Messages.TclChecker_scanning, tokens.get(2).getText(),
String.valueOf(getModuleCount() - scanned)));
scanned++;
} else if (PROGRESS_CHECKING.equals(progressSubCmd)) {
subTask(NLS.bind(Messages.TclChecker_checking, tokens.get(2).getText(),
String.valueOf(getModuleCount() - checked)));
++checked;
}
}
}
}
}
private void processMessage(final Map<String, IToken> attributes) throws IllegalArgumentException, CoreException {
final String file = attributes.get(ATTR_FILE).getText();
final int lineNumber = parseInt(attributes.get(ATTR_LINE));
if (lineNumber < 0) {
return;
}
final ISourceModule module = findSourceModule(new Path(file));
if (module == null) {
return;
}
final String messageId = attributes.get(ATTR_MESSAGE_ID).getText();
final TclCheckerProblem problem = new TclCheckerProblem(file, lineNumber, messageId,
attributes.get(ATTR_MESSAGE_TEXT).getText());
final Coord commandStart = parseCoord(attributes.get(ATTR_COMMAND_START));
final Coord commandEnd = parseCoord(attributes.get(ATTR_COMMAND_END));
if (commandStart != null && commandEnd != null) {
problem.setRange(new CoordRange(commandStart, commandEnd));
if (attributes.containsKey(ATTR_SUGGESTED_CORRECTIONS)) {
final List<IToken> correctionList;
final IToken corrections = attributes.get(ATTR_SUGGESTED_CORRECTIONS);
if (corrections.hasChildren()) {
correctionList = corrections.getChildren();
} else {
correctionList = Collections.singletonList(corrections);
}
final String[] suggestions = new String[correctionList.size()];
for (int i = 0; i < correctionList.size(); ++i) {
suggestions[i] = correctionList.get(i).getText();
}
final ISourceLineTracker lineTracker = lineTrackerFactory.getLineTracker(module);
problem.addAttribute(TclCheckerMarker.SUGGESTED_CORRECTIONS,
CorrectionEngine.encodeArguments(suggestions));
problem.addAttribute(TclCheckerMarker.COMMAND_START,
lineTrackerFactory.calculateOffset(lineTracker, commandStart));
problem.addAttribute(TclCheckerMarker.COMMAND_END,
lineTrackerFactory.calculateOffset(lineTracker, commandEnd));
problem.addAttribute(TclCheckerMarker.MESSAGE_ID, messageId);
problem.addAttribute(TclCheckerMarker.TIMESTAMP,
String.valueOf(module.getResource().getModificationStamp()));
problem.addAttribute(TclCheckerMarker.AUTO_CORRECTABLE,
attributes.get(ATTR_AUTO_CORRECTABLE).getText());
}
}
final Coord errorStart = parseCoord(attributes.get(ATTR_ERROR_START));
final Coord errorEnd = parseCoord(attributes.get(ATTR_ERROR_END));
if (errorStart != null && errorEnd != null) {
problem.setErrorRange(new CoordRange(errorStart, errorEnd));
}
reporter.report(module, problem);
}
private int parseInt(final IToken token) {
try {
return Integer.parseInt(token.getText());
} catch (NumberFormatException e) {
return -1;
}
}
private Coord parseCoord(final IToken token) {
if (token != null) {
final String s = token.getText();
final int pos = s.indexOf(' ');
if (pos > 0) {
try {
return new Coord(Integer.parseInt(s.substring(0, pos)), Integer.parseInt(s.substring(pos + 1)));
} catch (NumberFormatException e) {
// fall through
}
}
}
return null;
}
/**
* @param children
* @return
*/
private Map<String, IToken> parseAttributes(List<IToken> children) {
final Map<String, IToken> result = new HashMap<>();
for (int i = 0; i + 1 < children.size(); i += 2) {
final IToken key = children.get(i);
final IToken value = children.get(i + 1);
if (!key.hasChildren()) {
result.put(key.getText(), value);
}
}
return result;
}
private static final String CMD_MESSAGE = "message"; //$NON-NLS-1$
private static final String CMD_PROGRESS = "progress"; //$NON-NLS-1$
private static final String PROGRESS_SCANNING = "scanning"; //$NON-NLS-1$
private static final String PROGRESS_CHECKING = "checking"; //$NON-NLS-1$
private static final String ATTR_FILE = "file"; //$NON-NLS-1$
private static final String ATTR_LINE = "line"; //$NON-NLS-1$
private static final String ATTR_MESSAGE_ID = "messageID"; //$NON-NLS-1$
private static final String ATTR_MESSAGE_TEXT = "messageText"; //$NON-NLS-1$
private static final String ATTR_COMMAND_START = "commandStart,portable"; //$NON-NLS-1$
private static final String ATTR_COMMAND_END = "commandEnd,portable"; //$NON-NLS-1$
private static final String ATTR_ERROR_START = "errorStart,portable"; //$NON-NLS-1$
private static final String ATTR_ERROR_END = "errorEnd,portable"; //$NON-NLS-1$
private static final String ATTR_SUGGESTED_CORRECTIONS = "suggestedCorrections"; //$NON-NLS-1$
private static final String ATTR_AUTO_CORRECTABLE = "autoCorrectable"; //$NON-NLS-1$
}