Files
processing4/java/src/processing/mode/java/PreprocSketch.java
T
2025-02-05 21:42:52 +01:00

292 lines
8.1 KiB
Java

package processing.mode.java;
import com.google.classpath.ClassPath;
import org.eclipse.jdt.core.compiler.IProblem;
import org.eclipse.jdt.core.dom.ASTNode;
import org.eclipse.jdt.core.dom.CompilationUnit;
import java.net.URLClassLoader;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import processing.app.Problem;
import processing.app.Sketch;
import processing.core.PApplet;
import processing.mode.java.preproc.ImportStatement;
import processing.mode.java.preproc.TextTransform.OffsetMapper;
public class PreprocSketch {
public final Sketch sketch;
public final CompilationUnit compilationUnit;
public final String[] classPathArray;
public final ClassPath classPath;
public final URLClassLoader classLoader;
public final String[] searchClassPathArray;
public final int[] tabStartOffsets;
public final String scrubbedPdeCode;
public final String pdeCode;
public final String javaCode;
public final OffsetMapper offsetMapper;
public final boolean hasSyntaxErrors;
public final boolean hasCompilationErrors;
public final List<ImportStatement> programImports;
public final List<ImportStatement> coreAndDefaultImports;
public final List<ImportStatement> codeFolderImports;
public final List<Problem> otherProblems;
public final List<IProblem> iproblems;
public final Map<String, Integer> javaFileMapping;
/// JAVA -> SKETCH -----------------------------------------------------------
public boolean inRange(SketchInterval interval) {
return interval != SketchInterval.BEFORE_START &&
interval.stopPdeOffset < pdeCode.length();
}
public String getPdeCode(SketchInterval si) {
if (si == SketchInterval.BEFORE_START) return "";
int stop = Math.min(si.stopPdeOffset, pdeCode.length());
int start = Math.min(si.startPdeOffset, stop);
return pdeCode.substring(start, stop);
}
public SketchInterval mapJavaToSketch(ASTNode node) {
return mapJavaToSketch(node.getStartPosition(),
node.getStartPosition() + node.getLength());
}
public boolean isJavaTab(String fileName) {
return javaFileMapping.containsKey(fileName);
}
public int getJavaTabIndex(String fileName) {
return javaFileMapping.get(fileName);
}
public SketchInterval mapJavaToSketch(IProblem iproblem) {
String originalFile = new String(iproblem.getOriginatingFileName());
boolean isJavaTab = javaFileMapping.containsKey(originalFile);
// If is a ".java" tab, do not need to map into combined sketch source.
if (isJavaTab) {
return new SketchInterval(
javaFileMapping.get(originalFile),
iproblem.getSourceStart(),
iproblem.getSourceEnd() + 1,
iproblem.getSourceStart(), // Is outside main sketch code
iproblem.getSourceEnd() + 1 // Is outside main sketch code
);
} else {
return mapJavaToSketch(
iproblem.getSourceStart(),
iproblem.getSourceEnd() + 1 // make it exclusive
);
}
}
public SketchInterval mapJavaToSketch(int startJavaOffset, int stopJavaOffset) {
int length = stopJavaOffset - startJavaOffset;
int startPdeOffset = javaOffsetToPdeOffset(startJavaOffset);
int stopPdeOffset;
if (length == 0) {
stopPdeOffset = startPdeOffset;
} else {
stopPdeOffset = javaOffsetToPdeOffset(stopJavaOffset-1);
if (stopPdeOffset >= 0 && (stopPdeOffset > startPdeOffset || length == 1)) {
stopPdeOffset += 1;
}
}
if (startPdeOffset < 0 || stopPdeOffset < 0) {
return SketchInterval.BEFORE_START;
}
int tabIndex = pdeOffsetToTabIndex(startPdeOffset);
if (startPdeOffset >= pdeCode.length()) {
startPdeOffset = pdeCode.length() - 1;
stopPdeOffset = startPdeOffset + 1;
}
return new SketchInterval(tabIndex,
pdeOffsetToTabOffset(tabIndex, startPdeOffset),
pdeOffsetToTabOffset(tabIndex, stopPdeOffset),
startPdeOffset, stopPdeOffset);
}
private int javaOffsetToPdeOffset(int javaOffset) {
return offsetMapper.getInputOffset(javaOffset);
}
public int pdeOffsetToTabIndex(int pdeOffset) {
pdeOffset = Math.max(0, pdeOffset);
int tab = Arrays.binarySearch(tabStartOffsets, pdeOffset);
if (tab < 0) {
tab = -(tab + 1) - 1;
}
return tab;
}
public int pdeOffsetToTabOffset(int tabIndex, int pdeOffset) {
int tabStartOffset = tabStartOffsets[clipTabIndex(tabIndex)];
return pdeOffset - tabStartOffset;
}
/// SKETCH -> JAVA -----------------------------------------------------------
public int tabOffsetToJavaOffset(int tabIndex, int tabOffset) {
int tabStartOffset = tabStartOffsets[clipTabIndex(tabIndex)];
int pdeOffset = tabStartOffset + tabOffset;
return offsetMapper.getOutputOffset(pdeOffset);
}
/// LINE NUMBERS -------------------------------------------------------------
public int tabOffsetToJavaLine(int tabIndex, int tabOffset) {
int javaOffset = tabOffsetToJavaOffset(tabIndex, tabOffset);
return offsetToLine(javaCode, javaOffset);
}
public int tabOffsetToTabLine(int tabIndex, int tabOffset) {
int tabStartOffset = tabStartOffsets[clipTabIndex(tabIndex)];
return offsetToLine(pdeCode, tabStartOffset, tabStartOffset + tabOffset);
}
// TODO: optimize
private static int offsetToLine(String text, int offset) {
return offsetToLine(text, 0, offset);
}
// TODO: optimize
private static int offsetToLine(String text, int start, int offset) {
int line = 0;
while (offset >= start) {
offset = text.lastIndexOf('\n', offset-1);
line++;
}
return line - 1;
}
/// Util ---------------------------------------------------------------------
private int clipTabIndex(int tabIndex) {
return PApplet.constrain(tabIndex, 0, tabStartOffsets.length - 1);
}
/// BUILDER BUSINESS /////////////////////////////////////////////////////////
/**
* There is a lot of fields and having constructor with this many parameters
* is just not practical. Fill stuff into builder and then simply build it.
* Builder also guards against calling methods in the middle of building process.
*/
public static class Builder {
public Sketch sketch;
public CompilationUnit compilationUnit;
public String[] classPathArray;
public ClassPath classPath;
public URLClassLoader classLoader;
public String[] searchClassPathArray;
public int[] tabStartOffsets = new int[0];
public String scrubbedPdeCode;
public String pdeCode;
public String javaCode;
public OffsetMapper offsetMapper;
public boolean hasSyntaxErrors;
public boolean hasCompilationErrors;
public final List<ImportStatement> programImports = new ArrayList<>();
public final List<ImportStatement> coreAndDefaultImports = new ArrayList<>();
public final List<ImportStatement> codeFolderImports = new ArrayList<>();
public final List<Problem> otherProblems = new ArrayList<>();
public List<IProblem> iproblems;
public Map<String, Integer> javaFileMapping;
public PreprocSketch build() {
return new PreprocSketch(this);
}
}
public static PreprocSketch empty() {
return new Builder().build();
}
private PreprocSketch(Builder b) {
sketch = b.sketch;
compilationUnit = b.compilationUnit;
classPathArray = b.classPathArray;
classPath = b.classPath;
classLoader = b.classLoader;
searchClassPathArray = b.searchClassPathArray;
tabStartOffsets = b.tabStartOffsets;
scrubbedPdeCode = b.scrubbedPdeCode;
pdeCode = b.pdeCode;
javaCode = b.javaCode;
offsetMapper = b.offsetMapper != null ? b.offsetMapper : OffsetMapper.EMPTY_MAPPER;
hasSyntaxErrors = b.hasSyntaxErrors;
hasCompilationErrors = b.hasCompilationErrors;
otherProblems = b.otherProblems;
javaFileMapping = b.javaFileMapping;
iproblems = b.iproblems;
programImports = Collections.unmodifiableList(b.programImports);
coreAndDefaultImports = Collections.unmodifiableList(b.coreAndDefaultImports);
codeFolderImports = Collections.unmodifiableList(b.codeFolderImports);
}
}