mirror of
https://github.com/processing/processing4.git
synced 2026-02-04 06:09:17 +01:00
ECS: New preprocessing pipeline (breaking)
Breaks ASTGenerator and offset mapping everywhere
This commit is contained in:
@@ -106,11 +106,10 @@ import com.google.classpath.ClassPath;
|
||||
import com.google.classpath.ClassPathFactory;
|
||||
import com.google.classpath.RegExpResourceFilter;
|
||||
|
||||
|
||||
@SuppressWarnings({ "unchecked" })
|
||||
public class ASTGenerator {
|
||||
|
||||
public static final boolean SHOW_DEBUG_TREE = true;
|
||||
public static final boolean SHOW_DEBUG_TREE = false;
|
||||
|
||||
protected final ErrorCheckerService errorCheckerService;
|
||||
protected final JavaEditor editor;
|
||||
@@ -143,7 +142,7 @@ public class ASTGenerator {
|
||||
List<VariableDeclarationFragment> vdfs = null;
|
||||
switch (node.getNodeType()) {
|
||||
case ASTNode.TYPE_DECLARATION:
|
||||
return new CompletionCandidate[] { new CompletionCandidate((TypeDeclaration) node) };
|
||||
return new CompletionCandidate[]{new CompletionCandidate((TypeDeclaration) node)};
|
||||
|
||||
case ASTNode.METHOD_DECLARATION:
|
||||
MethodDeclaration md = (MethodDeclaration) node;
|
||||
@@ -155,12 +154,12 @@ public class ASTGenerator {
|
||||
for (int i = 0; i < params.size(); i++) {
|
||||
// cand[i + 1] = new CompletionCandidate(params.get(i).toString(), "", "",
|
||||
// CompletionCandidate.LOCAL_VAR);
|
||||
cand[i + 1] = new CompletionCandidate((SingleVariableDeclaration)params.get(i));
|
||||
cand[i + 1] = new CompletionCandidate((SingleVariableDeclaration) params.get(i));
|
||||
}
|
||||
return cand;
|
||||
|
||||
case ASTNode.SINGLE_VARIABLE_DECLARATION:
|
||||
return new CompletionCandidate[] { new CompletionCandidate((SingleVariableDeclaration)node) };
|
||||
return new CompletionCandidate[]{new CompletionCandidate((SingleVariableDeclaration) node)};
|
||||
|
||||
case ASTNode.FIELD_DECLARATION:
|
||||
vdfs = ((FieldDeclaration) node).fragments();
|
||||
@@ -757,8 +756,8 @@ public class ASTGenerator {
|
||||
}
|
||||
|
||||
log("Looking in the classloader for " + className);
|
||||
ArrayList<ImportStatement> imports = errorCheckerService
|
||||
.getProgramImports();
|
||||
// TODO: get this from last code check result
|
||||
List<ImportStatement> imports = Collections.emptyList(); //errorCheckerService.getProgramImports();
|
||||
|
||||
for (ImportStatement impS : imports) {
|
||||
String temp = impS.getPackageName();
|
||||
@@ -769,7 +768,7 @@ public class ASTGenerator {
|
||||
continue;
|
||||
}
|
||||
} else { // case of class import: pkg.foo.MyClass
|
||||
if (!impS.getImportedClassName().equals(className)) {
|
||||
if (!impS.getClassName().equals(className)) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
@@ -781,7 +780,9 @@ public class ASTGenerator {
|
||||
//log("Doesn't exist in imp package: " + impS.getImportName());
|
||||
}
|
||||
|
||||
for (ImportStatement impS : errorCheckerService.codeFolderImports) {
|
||||
// TODO: get this from last code check result
|
||||
List<ImportStatement> codeFolderImports = Collections.emptyList();
|
||||
for (ImportStatement impS : codeFolderImports) {
|
||||
String temp = impS.getPackageName();
|
||||
if (impS.isStarredImport()) { // case of starred import: pkg.foo.*
|
||||
if (className.indexOf('.') == -1) {
|
||||
@@ -790,7 +791,7 @@ public class ASTGenerator {
|
||||
continue;
|
||||
}
|
||||
} else { // case of class import: pkg.foo.MyClass
|
||||
if (!impS.getImportedClassName().equals(className)) {
|
||||
if (!impS.getClassName().equals(className)) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
@@ -840,8 +841,10 @@ public class ASTGenerator {
|
||||
Class<?> tehClass = null;
|
||||
if (className != null) {
|
||||
try {
|
||||
tehClass = Class.forName(className, false,
|
||||
errorCheckerService.getSketchClassLoader());
|
||||
// TODO: get the class loader from the last code check result
|
||||
/*tehClass = Class.forName(className, false,
|
||||
errorCheckerService.getSketchClassLoader());*/
|
||||
tehClass = Class.forName(className);
|
||||
} catch (ClassNotFoundException e) {
|
||||
//log("Doesn't exist in package: ");
|
||||
}
|
||||
|
||||
@@ -25,7 +25,6 @@ import java.io.ByteArrayOutputStream;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.net.MalformedURLException;
|
||||
import java.net.URL;
|
||||
import java.net.URLClassLoader;
|
||||
@@ -42,11 +41,10 @@ import java.util.concurrent.Executors;
|
||||
import java.util.concurrent.ScheduledExecutorService;
|
||||
import java.util.concurrent.ScheduledFuture;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import javax.swing.event.DocumentEvent;
|
||||
import javax.swing.event.DocumentListener;
|
||||
import javax.swing.text.BadLocationException;
|
||||
import javax.swing.text.Document;
|
||||
import javax.swing.text.Element;
|
||||
import javax.swing.text.PlainDocument;
|
||||
@@ -73,15 +71,14 @@ import org.eclipse.jdt.internal.compiler.problem.DefaultProblemFactory;
|
||||
|
||||
import processing.app.Library;
|
||||
import processing.app.Messages;
|
||||
import processing.app.Platform;
|
||||
import processing.app.Preferences;
|
||||
import processing.app.Sketch;
|
||||
import processing.app.SketchCode;
|
||||
import processing.app.SketchException;
|
||||
import processing.app.Util;
|
||||
import processing.app.ui.Editor;
|
||||
import processing.app.ui.EditorStatus;
|
||||
import processing.app.ui.ErrorTable;
|
||||
import processing.core.PApplet;
|
||||
import processing.data.StringList;
|
||||
import processing.mode.java.JavaMode;
|
||||
import processing.mode.java.JavaEditor;
|
||||
@@ -104,23 +101,6 @@ public class ErrorCheckerService {
|
||||
*/
|
||||
private volatile boolean running;
|
||||
|
||||
//protected ErrorWindow errorWindow;
|
||||
|
||||
/**
|
||||
* Class name of current sketch
|
||||
*/
|
||||
protected String className;
|
||||
|
||||
/**
|
||||
* URLs of extra imports jar files stored here.
|
||||
*/
|
||||
protected URL[] classPath = {};
|
||||
|
||||
/**
|
||||
* Class loader used by compiler check and ASTGenerator, based on classPath
|
||||
*/
|
||||
protected URLClassLoader classLoader = new URLClassLoader(classPath);
|
||||
|
||||
/**
|
||||
* How many lines are present till the initial class declaration? In static
|
||||
* mode, this would include imports, class declaration and setup
|
||||
@@ -129,39 +109,6 @@ public class ErrorCheckerService {
|
||||
*/
|
||||
public int mainClassOffset;
|
||||
|
||||
/**
|
||||
* If true, compilation checker will be reloaded with updated classpath
|
||||
* items.
|
||||
*/
|
||||
protected boolean loadCompClass;
|
||||
|
||||
/**
|
||||
* List of jar files to be present in compilation checker's classpath
|
||||
*/
|
||||
protected List<URL> classpathJars = new ArrayList<>();
|
||||
|
||||
/**
|
||||
* Stores the current import statements in the program. Used to compare for
|
||||
* changed import statements and update classpath if needed.
|
||||
*/
|
||||
protected ArrayList<ImportStatement> programImports = new ArrayList<>();
|
||||
|
||||
/**
|
||||
* List of imports when sketch was last checked. Used for checking for
|
||||
* changed imports
|
||||
*/
|
||||
protected ArrayList<ImportStatement> previousImports = new ArrayList<>();
|
||||
|
||||
/**
|
||||
* List of import statements for any .jar files in the code folder.
|
||||
*/
|
||||
protected final ArrayList<ImportStatement> codeFolderImports = new ArrayList<>();
|
||||
|
||||
/**
|
||||
* Teh Preprocessor
|
||||
*/
|
||||
protected final XQPreprocessor xqpreproc;
|
||||
|
||||
/**
|
||||
* ASTGenerator for operations on AST
|
||||
*/
|
||||
@@ -170,15 +117,14 @@ public class ErrorCheckerService {
|
||||
/**
|
||||
* Regexp for import statements. (Used from Processing source)
|
||||
*/
|
||||
// TODO: merge this with SourceUtils one
|
||||
public static final String IMPORT_REGEX =
|
||||
"(?:^|;)\\s*(import\\s+)((?:static\\s+)?\\S+)(\\s*;)";
|
||||
|
||||
|
||||
public ErrorCheckerService(JavaEditor editor) {
|
||||
this.editor = editor;
|
||||
xqpreproc = new XQPreprocessor(editor);
|
||||
astGenerator = new ASTGenerator(editor, this);
|
||||
loadCompClass = true;
|
||||
}
|
||||
|
||||
|
||||
@@ -211,19 +157,15 @@ public class ErrorCheckerService {
|
||||
// This is when the loaded sketch already has syntax errors.
|
||||
// Completion wouldn't be complete, but it'd be still something
|
||||
// better than nothing
|
||||
try {
|
||||
{
|
||||
final DefaultMutableTreeNode tree =
|
||||
ASTGenerator.buildTree(lastCodeCheckResult.compilationUnit);
|
||||
EventQueue.invokeAndWait(new Runnable() {
|
||||
EventQueue.invokeLater(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
synchronized (astGenerator) {
|
||||
astGenerator.updateAST(lastCodeCheckResult.compilationUnit, tree);
|
||||
}
|
||||
astGenerator.updateAST(lastCodeCheckResult.compilationUnit, tree);
|
||||
}
|
||||
});
|
||||
} catch (InterruptedException | InvocationTargetException e) {
|
||||
Messages.loge("exception during initial AST update", e);
|
||||
}
|
||||
|
||||
while (running) {
|
||||
@@ -291,9 +233,10 @@ public class ErrorCheckerService {
|
||||
synchronized (astGenerator) {
|
||||
astGenerator.getGui().disposeAllWindows();
|
||||
}
|
||||
classLoader = null;
|
||||
Messages.loge("Thread stopped: " + editor.getSketch().getName());
|
||||
|
||||
// TODO: clear last result
|
||||
|
||||
running = false;
|
||||
}
|
||||
};
|
||||
@@ -391,10 +334,76 @@ public class ErrorCheckerService {
|
||||
|
||||
CodeCheckResult result = new CodeCheckResult();
|
||||
|
||||
result.sourceCode = preprocessCode();
|
||||
StringBuilder rawCode = new StringBuilder();
|
||||
List<ImportStatement> coreAndDefaultImports = new ArrayList<>();
|
||||
List<ImportStatement> codeFolderImports = new ArrayList<>();
|
||||
List<ImportStatement> programImports = new ArrayList<>();
|
||||
|
||||
char[] sourceCodeArray = result.sourceCode.toCharArray();
|
||||
Sketch sketch = editor.getSketch();
|
||||
|
||||
{ // Combine code into one buffer
|
||||
for (SketchCode sc : sketch.getCode()) {
|
||||
if (sc.isExtension("pde")) {
|
||||
if (sketch.getCurrentCode().equals(sc)) {
|
||||
try {
|
||||
rawCode.append(sc.getDocumentText());
|
||||
} catch (BadLocationException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
} else {
|
||||
rawCode.append(sc.getProgram());
|
||||
}
|
||||
rawCode.append('\n');
|
||||
}
|
||||
}
|
||||
}
|
||||
String className = sketch.getName();
|
||||
|
||||
{ // Prepare core and default imports
|
||||
PdePreprocessor p = editor.createPreprocessor(null);
|
||||
String[] defaultImports = p.getDefaultImports();
|
||||
String[] coreImports = p.getCoreImports();
|
||||
|
||||
for (String imp : coreImports) {
|
||||
coreAndDefaultImports.add(ImportStatement.parse(imp));
|
||||
}
|
||||
for (String imp : defaultImports) {
|
||||
coreAndDefaultImports.add(ImportStatement.parse(imp));
|
||||
}
|
||||
}
|
||||
|
||||
{ // Prepare code folder imports & add to classpath
|
||||
if (sketch.hasCodeFolder()) {
|
||||
File codeFolder = sketch.getCodeFolder();
|
||||
String codeFolderClassPath = Util.contentsToClassPath(codeFolder);
|
||||
StringList codeFolderPackages = Util.packageListFromClassPath(codeFolderClassPath);
|
||||
for (String item : codeFolderPackages) {
|
||||
codeFolderImports.add(ImportStatement.wholePackage(item));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
SourceUtils.substituteUnicode(rawCode);
|
||||
|
||||
try {
|
||||
SourceUtils.scrubComments(rawCode);
|
||||
} catch (RuntimeException e) {
|
||||
// TODO: Unterminated block comment: add to errors
|
||||
// Continue normally, comments were scrubbed
|
||||
}
|
||||
|
||||
PdePreprocessor.Mode mode = PdePreprocessor.parseMode(rawCode);
|
||||
|
||||
SourceMapping mapping = new SourceMapping();
|
||||
|
||||
mapping.addAll(SourceUtils.insertImports(coreAndDefaultImports));
|
||||
mapping.addAll(SourceUtils.insertImports(codeFolderImports));
|
||||
mapping.addAll(SourceUtils.parseProgramImports(rawCode, programImports));
|
||||
mapping.addAll(SourceUtils.replaceTypeConstructors(rawCode));
|
||||
mapping.addAll(SourceUtils.replaceHexLiterals(rawCode));
|
||||
mapping.addAll(SourceUtils.wrapSketch(mode, className, rawCode.length()));
|
||||
|
||||
result.sourceCode = mapping.apply(rawCode);
|
||||
|
||||
List<IProblem> problems;
|
||||
|
||||
@@ -403,13 +412,12 @@ public class ErrorCheckerService {
|
||||
result.syntaxErrors = true;
|
||||
result.containsErrors = true;
|
||||
|
||||
parser.setSource(sourceCodeArray);
|
||||
parser.setSource(result.sourceCode.toCharArray());
|
||||
parser.setKind(ASTParser.K_COMPILATION_UNIT);
|
||||
parser.setCompilerOptions(COMPILER_OPTIONS);
|
||||
parser.setStatementsRecovery(true);
|
||||
|
||||
result.compilationUnit = (CompilationUnit) parser.createAST(null);
|
||||
|
||||
// Store errors returned by the ast parser
|
||||
problems = Arrays.asList(result.compilationUnit.getProblems());
|
||||
|
||||
@@ -425,53 +433,60 @@ public class ErrorCheckerService {
|
||||
|
||||
// No syntax errors, proceed for compilation check, Stage 2.
|
||||
if (problems.isEmpty() && !editor.hasJavaTabs()) {
|
||||
String sourceCode = xqpreproc.handle(result.sourceCode, programImports);
|
||||
prepareCompilerClasspath();
|
||||
|
||||
{{ // COMPILE CHECK
|
||||
SourceMapping mapping2 = new SourceMapping();
|
||||
|
||||
parser.setSource(sourceCodeArray);
|
||||
mapping2.addAll(SourceUtils.addPublicToTopLeveMethods(result.compilationUnit));
|
||||
mapping2.addAll(SourceUtils.replaceColorAndFixFloats(result.compilationUnit));
|
||||
|
||||
result.sourceCode = mapping2.apply(result.sourceCode);
|
||||
|
||||
char[] chars = result.sourceCode.toCharArray();
|
||||
|
||||
{ // Recreate the compilation unit for code completion, etc.
|
||||
parser.setSource(chars);
|
||||
parser.setKind(ASTParser.K_COMPILATION_UNIT);
|
||||
parser.setCompilerOptions(COMPILER_OPTIONS);
|
||||
parser.setStatementsRecovery(true);
|
||||
|
||||
result.compilationUnit = (CompilationUnit) parser.createAST(null);
|
||||
result.sourceCode = sourceCode;
|
||||
}
|
||||
|
||||
// Currently (Sept, 2012) I'm using Java's reflection api to load the
|
||||
// CompilationChecker class(from CompilationChecker.jar) that houses the
|
||||
// Eclispe JDT compiler, and call its getErrorsAsObj method to obtain
|
||||
// errors. This way, I'm able to add the paths of contributed libraries
|
||||
// to the classpath of CompilationChecker, dynamically. The eclipse compiler
|
||||
// needs all referenced libraries in the classpath. Totally a hack. If you find
|
||||
// a better method, do let me know.
|
||||
// TODO: set astGenerator.classPath (do this probably on EDT in UI update)
|
||||
// new ClassPathFactory().createFromPath((String) classPath);
|
||||
|
||||
{{ // COMPILE CHECK
|
||||
|
||||
// TODO: get these form last result
|
||||
//boolean importsChanged = checkIfImportsChanged(programImports, prevProgramImports);
|
||||
//importsChanged &= checkIfImportsChanged(codeFolderImports, prevCodeFolderImports);
|
||||
|
||||
boolean importsChanged = true;
|
||||
|
||||
List<String> classPath;
|
||||
URLClassLoader classLoader = null;
|
||||
if (!importsChanged) {
|
||||
// TODO: set classPath to previous classPath
|
||||
} else {
|
||||
classPath = prepareCompilerClasspath(programImports, sketch);
|
||||
List<URL> urls = new ArrayList<>();
|
||||
for (String path : classPath) {
|
||||
try {
|
||||
urls.add(new File(path).toURI().toURL());
|
||||
} catch (MalformedURLException e) {
|
||||
// Malformed, ignore
|
||||
}
|
||||
}
|
||||
URL[] classPathArray = urls.toArray(new URL[urls.size()]);
|
||||
classLoader = new URLClassLoader(classPathArray, null);
|
||||
}
|
||||
|
||||
try {
|
||||
// NOTE TO SELF: If classpath contains null Strings
|
||||
// URLClassLoader shoots NPE bullets.
|
||||
|
||||
// If imports have changed, reload classes with new classpath.
|
||||
if (loadCompClass) {
|
||||
classPath = new URL[classpathJars.size()];
|
||||
/*System.out.println("CP Jars:");
|
||||
for (URL u: classpathJars) {
|
||||
String fn = u.getFile();
|
||||
System.out.println(fn.substring(fn.lastIndexOf('/')));
|
||||
}*/
|
||||
classPath = classpathJars.toArray(classPath);
|
||||
classLoader = new URLClassLoader(classPath, null);
|
||||
loadCompClass = false;
|
||||
}
|
||||
|
||||
problems = compileAndReturnProblems(className, sourceCode,
|
||||
problems = compileAndReturnProblems(className, chars,
|
||||
COMPILER_OPTIONS, classLoader);
|
||||
} catch (Exception e) {
|
||||
System.err.println("compileCheck() problem." + e);
|
||||
e.printStackTrace();
|
||||
cancel();
|
||||
} catch (NoClassDefFoundError e) {
|
||||
// TODO: do we need this?
|
||||
e.printStackTrace();
|
||||
cancel();
|
||||
}
|
||||
}}
|
||||
}
|
||||
@@ -521,7 +536,7 @@ public class ErrorCheckerService {
|
||||
* @return list of compiler errors and warnings
|
||||
*/
|
||||
static public List<IProblem> compileAndReturnProblems(String sourceName,
|
||||
String source,
|
||||
char[] source,
|
||||
Map<String, String> options,
|
||||
URLClassLoader classLoader) {
|
||||
final List<IProblem> problems = new ArrayList<>();
|
||||
@@ -533,7 +548,7 @@ public class ErrorCheckerService {
|
||||
}
|
||||
};
|
||||
|
||||
final char[] contents = source.toCharArray();
|
||||
final char[] contents = source;
|
||||
final char[][] packageName = new char[][]{};
|
||||
final char[] mainTypeName = sourceName.toCharArray();
|
||||
final char[] fileName = (sourceName + ".java").toCharArray();
|
||||
@@ -552,6 +567,7 @@ public class ErrorCheckerService {
|
||||
new CompilerOptions(options),
|
||||
requestor,
|
||||
new DefaultProblemFactory(Locale.getDefault()));
|
||||
|
||||
compiler.compile(new ICompilationUnit[]{unit});
|
||||
return problems;
|
||||
}
|
||||
@@ -624,140 +640,83 @@ public class ErrorCheckerService {
|
||||
}
|
||||
|
||||
|
||||
public URLClassLoader getSketchClassLoader() {
|
||||
return classLoader;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Processes import statements to obtain class paths of contributed
|
||||
* libraries. This would be needed for compilation check. Also, adds
|
||||
* stuff(jar files, class files, candy) from the code folder. And it looks
|
||||
* messed up.
|
||||
*/
|
||||
protected void prepareCompilerClasspath() {
|
||||
if (!loadCompClass) {
|
||||
return;
|
||||
}
|
||||
protected List<String> prepareCompilerClasspath(List<ImportStatement> programImports, Sketch sketch) {
|
||||
JavaMode mode = (JavaMode) editor.getMode();
|
||||
|
||||
StringBuilder classPath = new StringBuilder();
|
||||
|
||||
// log("1..");
|
||||
classpathJars = new ArrayList<>();
|
||||
String entry;
|
||||
// boolean codeFolderChecked = false;
|
||||
for (ImportStatement impstat : programImports) {
|
||||
String item = impstat.getImportName();
|
||||
int dot = item.lastIndexOf('.');
|
||||
entry = (dot == -1) ? item : item.substring(0, dot);
|
||||
String entry = impstat.getPackageName();
|
||||
if (!ignorableImport(entry)) {
|
||||
|
||||
entry = entry.substring(6).trim();
|
||||
// log("Entry--" + entry);
|
||||
if (ignorableImport(entry)) {
|
||||
// log("Ignoring: " + entry);
|
||||
continue;
|
||||
}
|
||||
|
||||
// Try to get the library classpath and add it to the list
|
||||
try {
|
||||
Library library = editor.getMode().getLibrary(entry);
|
||||
String[] libraryPath =
|
||||
PApplet.split(library.getClassPath().substring(1).trim(),
|
||||
File.pathSeparatorChar);
|
||||
for (String pathItem : libraryPath) {
|
||||
classpathJars.add(new File(pathItem).toURI().toURL());
|
||||
// Try to get the library classpath and add it to the list
|
||||
try {
|
||||
Library library = mode.getLibrary(entry);
|
||||
classPath.append(library.getClassPath());
|
||||
} catch (SketchException e) {
|
||||
// More libraries competing, ignore
|
||||
}
|
||||
} catch (Exception e) {
|
||||
Messages.log("Encountered " + e + " while adding library to classpath");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Look around in the code folder for jar files and them too
|
||||
if (editor.getSketch().hasCodeFolder()) {
|
||||
File codeFolder = editor.getSketch().getCodeFolder();
|
||||
|
||||
// get a list of .jar files in the "code" folder
|
||||
// (class files in subfolders should also be picked up)
|
||||
if (sketch.hasCodeFolder()) {
|
||||
File codeFolder = sketch.getCodeFolder();
|
||||
String codeFolderClassPath = Util.contentsToClassPath(codeFolder);
|
||||
// codeFolderChecked = true;
|
||||
// huh? doesn't this mean .length() == 0? [fry]
|
||||
if (!codeFolderClassPath.equalsIgnoreCase("")) {
|
||||
Messages.log("Sketch has a code folder. Adding its jars");
|
||||
String codeFolderPath[] =
|
||||
PApplet.split(codeFolderClassPath.substring(1).trim(),
|
||||
File.pathSeparatorChar);
|
||||
try {
|
||||
for (String pathItem : codeFolderPath) {
|
||||
classpathJars.add(new File(pathItem).toURI().toURL());
|
||||
Messages.log("Addind cf jar: " + pathItem);
|
||||
}
|
||||
} catch (Exception e2) {
|
||||
e2.printStackTrace();
|
||||
}
|
||||
}
|
||||
classPath.append(codeFolderClassPath);
|
||||
}
|
||||
|
||||
// Also add jars specified in mode's search path
|
||||
String searchPath = ((JavaMode) editor.getMode()).getSearchPath();
|
||||
// TODO: maybe we need mode.getCoreLibrary().getClassPath() here
|
||||
String searchPath = mode.getSearchPath();
|
||||
if (searchPath != null) {
|
||||
String[] modeJars = PApplet.split(searchPath, File.pathSeparatorChar);
|
||||
for (String mj : modeJars) {
|
||||
try {
|
||||
classpathJars.add(new File(mj).toURI().toURL());
|
||||
} catch (MalformedURLException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
if (!searchPath.startsWith(File.pathSeparator)) {
|
||||
classPath.append(File.pathSeparator);
|
||||
}
|
||||
classPath.append(searchPath);
|
||||
}
|
||||
|
||||
// TODO: maybe we need lib.getClassPath() here
|
||||
for (Library lib : mode.coreLibraries) {
|
||||
classPath.append(File.pathSeparator).append(lib.getJarPath());
|
||||
}
|
||||
|
||||
String javaClassPath = System.getProperty("java.class.path");
|
||||
if (!javaClassPath.startsWith(File.pathSeparator)) {
|
||||
classPath.append(File.pathSeparator);
|
||||
}
|
||||
classPath.append(javaClassPath);
|
||||
|
||||
String rtPath = System.getProperty("java.home") +
|
||||
File.separator + "lib" + File.separator + "rt.jar";
|
||||
if (new File(rtPath).exists()) {
|
||||
classPath.append(File.pathSeparator).append(rtPath);
|
||||
} else {
|
||||
rtPath = System.getProperty("java.home") + File.separator + "jre" +
|
||||
File.separator + "lib" + File.separator + "rt.jar";
|
||||
if (new File(rtPath).exists()) {
|
||||
classPath.append(File.pathSeparator).append(rtPath);
|
||||
}
|
||||
}
|
||||
|
||||
for (Library lib : editor.getMode().coreLibraries) {
|
||||
try {
|
||||
classpathJars.add(new File(lib.getJarPath()).toURI().toURL());
|
||||
} catch (MalformedURLException e) {
|
||||
e.printStackTrace();
|
||||
// Make sure class path does not contain empty string (home dir)
|
||||
String[] paths = classPath.toString().split(File.pathSeparator);
|
||||
|
||||
List<String> entries = new ArrayList<>();
|
||||
|
||||
for (int i = 0; i < paths.length; i++) {
|
||||
String path = paths[i];
|
||||
if (path != null && !path.trim().isEmpty()) {
|
||||
entries.add(path);
|
||||
}
|
||||
}
|
||||
|
||||
StringList entries = new StringList();
|
||||
entries.append(System.getProperty("java.class.path").split(File.pathSeparator));
|
||||
entries.append(System.getProperty("java.home") +
|
||||
File.separator + "lib" + File.separator + "rt.jar");
|
||||
|
||||
String modeClassPath = ((JavaMode) editor.getMode()).getSearchPath();
|
||||
if (modeClassPath != null) {
|
||||
entries.append(modeClassPath);
|
||||
}
|
||||
|
||||
for (URL jarPath : classpathJars) {
|
||||
entries.append(jarPath.getPath());
|
||||
}
|
||||
|
||||
// // Just in case, make sure we don't run off into oblivion
|
||||
// String workingDirectory = System.getProperty("user.dir");
|
||||
// if (entries.removeValue(workingDirectory) != -1) {
|
||||
// System.err.println("user.dir found in classpath");
|
||||
// }
|
||||
|
||||
// // hm, these weren't problematic either
|
||||
// entries.append(System.getProperty("user.dir"));
|
||||
// entries.append("");
|
||||
// entries.print();
|
||||
|
||||
synchronized (astGenerator) {
|
||||
astGenerator.classPath = astGenerator.factory.createFromPath(entries.join(File.pathSeparator));
|
||||
Messages.log("Classpath created " + (astGenerator.classPath != null));
|
||||
Messages.log("Sketch classpath jars loaded.");
|
||||
if (Platform.isMacOS()) {
|
||||
File f = new File(System.getProperty("java.home") +
|
||||
File.separator + "bundle" +
|
||||
File.separator + "Classes" +
|
||||
File.separator + "classes.jar");
|
||||
Messages.log(f.getAbsolutePath() + " | classes.jar found?" + f.exists());
|
||||
} else {
|
||||
File f = new File(System.getProperty("java.home") + File.separator +
|
||||
"lib" + File.separator + "rt.jar" + File.separator);
|
||||
Messages.log(f.getAbsolutePath() + " | rt.jar found?" + f.exists());
|
||||
}
|
||||
}
|
||||
return entries;
|
||||
}
|
||||
|
||||
|
||||
@@ -771,7 +730,10 @@ public class ErrorCheckerService {
|
||||
|
||||
|
||||
protected boolean ignorableSuggestionImport(String impName) {
|
||||
String impNameLc = impName.toLowerCase();
|
||||
|
||||
// TODO: propagate these from last result
|
||||
|
||||
/*String impNameLc = impName.toLowerCase();
|
||||
|
||||
for (ImportStatement impS : programImports) {
|
||||
if (impNameLc.startsWith(impS.getPackageName().toLowerCase())) {
|
||||
@@ -783,7 +745,7 @@ public class ErrorCheckerService {
|
||||
if (impNameLc.startsWith(impS.getPackageName().toLowerCase())) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}*/
|
||||
|
||||
final String include = "include";
|
||||
final String exclude = "exclude";
|
||||
@@ -803,7 +765,7 @@ public class ErrorCheckerService {
|
||||
return true;
|
||||
}
|
||||
|
||||
static final Map<String, String> COMPILER_OPTIONS;
|
||||
static private final Map<String, String> COMPILER_OPTIONS;
|
||||
static {
|
||||
Map<String, String> compilerOptions = new HashMap<>();
|
||||
|
||||
@@ -952,6 +914,10 @@ public class ErrorCheckerService {
|
||||
* int[3] are on TODO
|
||||
*/
|
||||
protected int[] JavaToPdeOffsets(int line, int offset) {
|
||||
|
||||
return new int[] { 0, 0 }; // TODO
|
||||
|
||||
/*
|
||||
int codeIndex = 0;
|
||||
|
||||
int x = line - mainClassOffset;
|
||||
@@ -964,7 +930,7 @@ public class ErrorCheckerService {
|
||||
ImportStatement is = programImports.get(x);
|
||||
// log(is.importName + ", " + is.tab + ", "
|
||||
// + is.lineNumber);
|
||||
return new int[] { is.getTab(), is.getLineNumber() };
|
||||
return new int[] { 0, 0 }; // TODO
|
||||
} else {
|
||||
|
||||
// Some seriously ugly stray error, just can't find the source
|
||||
@@ -1020,6 +986,7 @@ public class ErrorCheckerService {
|
||||
e.printStackTrace();
|
||||
}
|
||||
return new int[] { codeIndex, x };
|
||||
*/
|
||||
}
|
||||
|
||||
|
||||
@@ -1039,6 +1006,11 @@ public class ErrorCheckerService {
|
||||
* @return int[0] - tab number, int[1] - line number
|
||||
*/
|
||||
protected int[] calculateTabIndexAndLineNumber(int javalineNumber) {
|
||||
|
||||
return new int[] { 0, 0 }; // TODO
|
||||
|
||||
/*
|
||||
|
||||
// String[] lines = {};// = PApplet.split(sourceString, '\n');
|
||||
int codeIndex = 0;
|
||||
|
||||
@@ -1052,7 +1024,7 @@ public class ErrorCheckerService {
|
||||
ImportStatement is = programImports.get(x);
|
||||
// log(is.importName + ", " + is.tab + ", "
|
||||
// + is.lineNumber);
|
||||
return new int[] { is.getTab(), is.getLineNumber() };
|
||||
return new int[] { 0, 0 }; // TODO
|
||||
} else {
|
||||
|
||||
// Some seriously ugly stray error, just can't find the source
|
||||
@@ -1105,6 +1077,8 @@ public class ErrorCheckerService {
|
||||
System.err.println("Things got messed up in ErrorCheckerService.calculateTabIndexAndLineNumber()");
|
||||
}
|
||||
return new int[] { codeIndex, x };
|
||||
|
||||
*/
|
||||
}
|
||||
|
||||
|
||||
@@ -1112,159 +1086,15 @@ public class ErrorCheckerService {
|
||||
* Returns line number of corresponding java source
|
||||
*/
|
||||
protected int getJavaLineNumFromPDElineNum(int tab, int pdeLineNum){
|
||||
int jLineNum = programImports.size() + 1;
|
||||
return 0;
|
||||
|
||||
/*int jLineNum = programImports.size() + 1;
|
||||
for (int i = 0; i < tab; i++) {
|
||||
SketchCode sc = editor.getSketch().getCode(i);
|
||||
int len = Util.countLines(sc.getProgram()) + 1;
|
||||
jLineNum += len;
|
||||
}
|
||||
return jLineNum;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Fetches code from the editor tabs and pre-processes it into parsable pure
|
||||
* java source. And there's a difference between parsable and compilable.
|
||||
* XQPrerocessor.java makes this code compilable. <br>
|
||||
* Handles: <li>Removal of import statements <li>Conversion of int(),
|
||||
* char(), etc to PApplet.parseInt(), etc. <li>Replacing '#' with 0xff for
|
||||
* color representation<li>Converts all 'color' datatypes to int
|
||||
* (experimental) <li>Appends class declaration statement after determining
|
||||
* the mode the sketch is in - ACTIVE or STATIC
|
||||
*
|
||||
* @return String - Pure java representation of PDE code. Note that this
|
||||
* code is not yet compile ready.
|
||||
*/
|
||||
protected String preprocessCode() {
|
||||
ArrayList<ImportStatement> scrappedImports = new ArrayList<>();
|
||||
StringBuilder rawCode = new StringBuilder();
|
||||
final Sketch sketch = editor.getSketch();
|
||||
try {
|
||||
for (SketchCode sc : sketch.getCode()) {
|
||||
if (sc.isExtension("pde")) {
|
||||
|
||||
try {
|
||||
if (sketch.getCurrentCode().equals(sc)) {
|
||||
rawCode.append(scrapImportStatements(sc.getDocumentText(),
|
||||
sketch.getCodeIndex(sc),
|
||||
scrappedImports));
|
||||
} else {
|
||||
rawCode.append(scrapImportStatements(sc.getProgram(),
|
||||
sketch.getCodeIndex(sc),
|
||||
scrappedImports));
|
||||
}
|
||||
rawCode.append('\n');
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
rawCode.append('\n');
|
||||
}
|
||||
}
|
||||
|
||||
} catch (Exception e) {
|
||||
Messages.log("Exception in preprocessCode()");
|
||||
}
|
||||
|
||||
// Swap atomically, might blow up anyway
|
||||
// TODO: this is iterated from multiple threads, synchronize properly
|
||||
this.programImports = scrappedImports;
|
||||
|
||||
String sourceAlt = rawCode.toString();
|
||||
// Replace comments with whitespaces
|
||||
// sourceAlt = scrubComments(sourceAlt);
|
||||
|
||||
// Find all int(*), replace with PApplet.parseInt(*)
|
||||
|
||||
// \bint\s*\(\s*\b , i.e all exclusive "int("
|
||||
|
||||
String dataTypeFunc[] = { "int", "char", "float", "boolean", "byte" };
|
||||
|
||||
for (String dataType : dataTypeFunc) {
|
||||
String dataTypeRegexp = "\\b" + dataType + "\\s*\\(";
|
||||
Pattern pattern = Pattern.compile(dataTypeRegexp);
|
||||
Matcher matcher = pattern.matcher(sourceAlt);
|
||||
|
||||
// while (matcher.find()) {
|
||||
// System.out.print("Start index: " + matcher.start());
|
||||
// log(" End index: " + matcher.end() + " ");
|
||||
// log("-->" + matcher.group() + "<--");
|
||||
// }
|
||||
sourceAlt = matcher.replaceAll("PApplet.parse"
|
||||
+ Character.toUpperCase(dataType.charAt(0))
|
||||
+ dataType.substring(1) + "(");
|
||||
|
||||
}
|
||||
|
||||
// Find all #[web color] and replace with 0xff[webcolor]
|
||||
// Should be 6 digits only.
|
||||
final String webColorRegexp = "#[A-Fa-f0-9]{6}\\W";
|
||||
Pattern webPattern = Pattern.compile(webColorRegexp);
|
||||
Matcher webMatcher = webPattern.matcher(sourceAlt);
|
||||
while (webMatcher.find()) {
|
||||
// log("Found at: " + webMatcher.start());
|
||||
String found = sourceAlt.substring(webMatcher.start(),
|
||||
webMatcher.end());
|
||||
// log("-> " + found);
|
||||
sourceAlt = webMatcher.replaceFirst("0xff" + found.substring(1));
|
||||
webMatcher = webPattern.matcher(sourceAlt);
|
||||
}
|
||||
|
||||
// Replace all color data types with int
|
||||
// Regex, Y U SO powerful?
|
||||
final String colorTypeRegex = "color(?![a-zA-Z0-9_])(?=\\[*)(?!(\\s*\\())";
|
||||
Pattern colorPattern = Pattern.compile(colorTypeRegex);
|
||||
Matcher colorMatcher = colorPattern.matcher(sourceAlt);
|
||||
sourceAlt = colorMatcher.replaceAll("int");
|
||||
|
||||
checkForChangedImports();
|
||||
|
||||
className = editor.getSketch().getName();
|
||||
|
||||
// Check whether the code is being written in STATIC mode
|
||||
PdePreprocessor.Mode mode;
|
||||
try {
|
||||
String uncommented = PdePreprocessor.scrubComments(sourceAlt);
|
||||
mode = PdePreprocessor.parseMode(uncommented);
|
||||
} catch (RuntimeException r) {
|
||||
String uncommented = PdePreprocessor.scrubComments(sourceAlt + "*/");
|
||||
mode = PdePreprocessor.parseMode(uncommented);
|
||||
}
|
||||
|
||||
StringBuilder sb = new StringBuilder();
|
||||
|
||||
// Imports
|
||||
sb.append(xqpreproc.prepareImports(scrappedImports));
|
||||
|
||||
// Header
|
||||
if (mode != PdePreprocessor.Mode.JAVA) {
|
||||
sb.append("public class ").append(className).append(" extends PApplet {\n");
|
||||
if (mode == PdePreprocessor.Mode.STATIC) {
|
||||
sb.append("public void setup() {\n");
|
||||
}
|
||||
}
|
||||
|
||||
// Grab the offset before adding contents of the editor
|
||||
mainClassOffset = 1;
|
||||
for (int i = 0; i < sb.length(); i++) {
|
||||
if (sb.charAt(i) == '\n') {
|
||||
mainClassOffset++;
|
||||
}
|
||||
}
|
||||
|
||||
// Editor content
|
||||
sb.append(sourceAlt);
|
||||
|
||||
// Footer
|
||||
if (mode != PdePreprocessor.Mode.JAVA) {
|
||||
if (mode == PdePreprocessor.Mode.STATIC) {
|
||||
// no noLoop() here so it does not tell you
|
||||
// "can't invoke noLoop() on obj" when you type "obj."
|
||||
sb.append("\n}");
|
||||
}
|
||||
sb.append("\n}");
|
||||
}
|
||||
|
||||
return substituteUnicode(sb.toString());
|
||||
return jLineNum;*/
|
||||
}
|
||||
|
||||
|
||||
@@ -1382,26 +1212,19 @@ public class ErrorCheckerService {
|
||||
* Checks if import statements in the sketch have changed. If they have,
|
||||
* compiler classpath needs to be updated.
|
||||
*/
|
||||
protected void checkForChangedImports() {
|
||||
// log("Imports: " + programImports.size() +
|
||||
// " Prev Imp: "
|
||||
// + previousImports.size());
|
||||
if (programImports.size() != previousImports.size()) {
|
||||
// log(1);
|
||||
loadCompClass = true;
|
||||
previousImports = programImports;
|
||||
protected static boolean checkIfImportsChanged(List<ImportStatement> prevImports,
|
||||
List<ImportStatement> imports) {
|
||||
if (imports.size() != prevImports.size()) {
|
||||
return true;
|
||||
} else {
|
||||
for (int i = 0; i < programImports.size(); i++) {
|
||||
if (!programImports.get(i).getImportName().equals(previousImports
|
||||
.get(i).getImportName())) {
|
||||
// log(2);
|
||||
loadCompClass = true;
|
||||
previousImports = programImports;
|
||||
break;
|
||||
int count = imports.size();
|
||||
for (int i = 0; i < count; i++) {
|
||||
if (!imports.get(i).isSameAs(prevImports.get(i))) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
// log("load..? " + loadCompClass);
|
||||
return false;
|
||||
}
|
||||
|
||||
protected int pdeImportsCount;
|
||||
@@ -1410,107 +1233,6 @@ public class ErrorCheckerService {
|
||||
return pdeImportsCount;
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes import statements from tabSource, replaces each with white spaces
|
||||
* and adds the import to the list of program imports
|
||||
*
|
||||
* @param tabProgram
|
||||
* - Code in a tab
|
||||
* @param tabNumber
|
||||
* - index of the tab
|
||||
* @return String - Tab code with imports replaced with white spaces
|
||||
*/
|
||||
protected String scrapImportStatements(String tabProgram,
|
||||
int tabNumber,
|
||||
List<ImportStatement> outImports) {
|
||||
//TODO: Commented out imports are still detected as main imports.
|
||||
pdeImportsCount = 0;
|
||||
String tabSource = tabProgram;
|
||||
do {
|
||||
// log("-->\n" + sourceAlt + "\n<--");
|
||||
String[] pieces = PApplet.match(tabSource, IMPORT_REGEX);
|
||||
|
||||
// Stop the loop if we've removed all the import lines
|
||||
if (pieces == null) {
|
||||
break;
|
||||
}
|
||||
|
||||
String piece = pieces[1] + pieces[2] + pieces[3];
|
||||
int len = piece.length(); // how much to trim out
|
||||
|
||||
// programImports.add(piece); // the package name
|
||||
|
||||
// find index of this import in the program
|
||||
int idx = tabSource.indexOf(piece);
|
||||
// System.out.print("Import -> " + piece);
|
||||
// log(" - "
|
||||
// + Base.countLines(tabSource.substring(0, idx)) + " tab "
|
||||
// + tabNumber);
|
||||
int lineCount = Util.countLines(tabSource.substring(0, idx));
|
||||
outImports.add(new ImportStatement(piece, tabNumber, lineCount));
|
||||
// Remove the import from the main program
|
||||
// Substitute with white spaces
|
||||
String whiteSpace = "";
|
||||
for (int j = 0; j < piece.length(); j++) {
|
||||
whiteSpace += " ";
|
||||
}
|
||||
tabSource = tabSource.substring(0, idx) + whiteSpace
|
||||
+ tabSource.substring(idx + len);
|
||||
pdeImportsCount++;
|
||||
} while (true);
|
||||
// log(tabSource);
|
||||
return tabSource;
|
||||
}
|
||||
|
||||
/**
|
||||
* Replaces non-ascii characters with their unicode escape sequences and
|
||||
* stuff. Used as it is from
|
||||
* processing.src.processing.mode.java.preproc.PdePreprocessor
|
||||
*
|
||||
* @param program
|
||||
* - Input String containing non ascii characters
|
||||
* @return String - Converted String
|
||||
*/
|
||||
protected static String substituteUnicode(String program) {
|
||||
// check for non-ascii chars (these will be/must be in unicode format)
|
||||
char p[] = program.toCharArray();
|
||||
int unicodeCount = 0;
|
||||
for (int i = 0; i < p.length; i++) {
|
||||
if (p[i] > 127) {
|
||||
unicodeCount++;
|
||||
}
|
||||
}
|
||||
if (unicodeCount == 0) {
|
||||
return program;
|
||||
}
|
||||
// if non-ascii chars are in there, convert to unicode escapes
|
||||
// add unicodeCount * 5.. replacing each unicode char
|
||||
// with six digit uXXXX sequence (xxxx is in hex)
|
||||
// (except for nbsp chars which will be a replaced with a space)
|
||||
int index = 0;
|
||||
char p2[] = new char[p.length + unicodeCount * 5];
|
||||
for (int i = 0; i < p.length; i++) {
|
||||
if (p[i] < 128) {
|
||||
p2[index++] = p[i];
|
||||
} else if (p[i] == 160) { // unicode for non-breaking space
|
||||
p2[index++] = ' ';
|
||||
} else {
|
||||
int c = p[i];
|
||||
p2[index++] = '\\';
|
||||
p2[index++] = 'u';
|
||||
char str[] = Integer.toHexString(c).toCharArray();
|
||||
// add leading zeros, so that the length is 4
|
||||
// for (int i = 0; i < 4 - str.length; i++) p2[index++] = '0';
|
||||
for (int m = 0; m < 4 - str.length; m++)
|
||||
p2[index++] = '0';
|
||||
System.arraycopy(str, 0, p2, index, str.length);
|
||||
index += str.length;
|
||||
}
|
||||
}
|
||||
return new String(p2, 0, index);
|
||||
}
|
||||
|
||||
|
||||
public void handleErrorCheckingToggle() {
|
||||
if (!JavaMode.errorCheckEnabled) {
|
||||
Messages.log(editor.getSketch().getName() + " Error Checker disabled.");
|
||||
@@ -1527,11 +1249,6 @@ public class ErrorCheckerService {
|
||||
}
|
||||
|
||||
|
||||
public ArrayList<ImportStatement> getProgramImports() {
|
||||
return programImports;
|
||||
}
|
||||
|
||||
|
||||
private static class NameEnvironmentImpl implements INameEnvironment {
|
||||
|
||||
private final ClassLoader classLoader;
|
||||
|
||||
@@ -20,84 +20,108 @@ along with this program; if not, write to the Free Software Foundation, Inc.
|
||||
|
||||
package processing.mode.java.pdex;
|
||||
|
||||
import java.util.regex.MatchResult;
|
||||
import java.util.regex.Matcher;
|
||||
|
||||
/**
|
||||
* Wrapper for import statements
|
||||
*
|
||||
*
|
||||
* @author Manindra Moharana <me@mkmoharana.com>
|
||||
*
|
||||
*/
|
||||
public class ImportStatement {
|
||||
/**
|
||||
* Ex: processing.opengl.*, java.util.*
|
||||
*/
|
||||
private String importName;
|
||||
|
||||
/**
|
||||
* Which tab does it belong to?
|
||||
*/
|
||||
private int tab;
|
||||
private static final String importKw = "import";
|
||||
private static final String staticKw = "static";
|
||||
|
||||
private boolean isClass;
|
||||
private boolean isStarred;
|
||||
private boolean isStatic;
|
||||
|
||||
/**
|
||||
* Line number(pde code) of the import
|
||||
* Full class name of the import with all packages
|
||||
* Ends with star for starred imports
|
||||
*/
|
||||
private int lineNumber;
|
||||
private String className;
|
||||
|
||||
/**
|
||||
*
|
||||
* @param importName - Ex: processing.opengl.*, java.util.*
|
||||
* @param tab - Which tab does it belong to?
|
||||
* @param lineNumber - Line number(pde code) of the import
|
||||
* Name of the package e.g. everything before last dot
|
||||
*/
|
||||
public ImportStatement(String importName, int tab, int lineNumber) {
|
||||
this.importName = importName;
|
||||
this.tab = tab;
|
||||
this.lineNumber = lineNumber;
|
||||
private String packageName;
|
||||
|
||||
private ImportStatement() { }
|
||||
|
||||
public static ImportStatement wholePackage(String pckg) {
|
||||
ImportStatement is = new ImportStatement();
|
||||
is.packageName = pckg;
|
||||
is.className = "*";
|
||||
is.isStarred = true;
|
||||
return is;
|
||||
}
|
||||
|
||||
public String getImportName() {
|
||||
return importName;
|
||||
}
|
||||
|
||||
|
||||
public static ImportStatement singleClass(String cls) {
|
||||
ImportStatement is = new ImportStatement();
|
||||
int lastDot = cls.lastIndexOf('.');
|
||||
is.className = lastDot >= 0 ? cls.substring(lastDot+1) : cls;
|
||||
is.packageName = lastDot >= 0 ? cls.substring(0, lastDot) : "";
|
||||
is.isClass = true;
|
||||
return is;
|
||||
}
|
||||
|
||||
|
||||
|
||||
public static ImportStatement parse(String importString) {
|
||||
Matcher matcher = SourceUtils.IMPORT_REGEX_NO_KEYWORD.matcher(importString);
|
||||
if (!matcher.find()) return null;
|
||||
|
||||
return parse(matcher.toMatchResult());
|
||||
}
|
||||
|
||||
public static ImportStatement parse(MatchResult match) {
|
||||
ImportStatement is = new ImportStatement();
|
||||
|
||||
is.isStatic = match.group(2) != null;
|
||||
String pckg = match.group(3);
|
||||
pckg = (pckg == null) ? "" : pckg.replaceAll("\\s","");
|
||||
is.packageName = pckg.endsWith(".") ?
|
||||
pckg.substring(0, pckg.length()-1) :
|
||||
pckg;
|
||||
|
||||
is.className = match.group(4);
|
||||
is.isStarred = is.className.equals("*");
|
||||
|
||||
return is;
|
||||
}
|
||||
|
||||
|
||||
|
||||
public String getFullSourceLine() {
|
||||
return importKw + " " + (isStatic ? (staticKw + " ") : "") + packageName + "." + className + ";";
|
||||
}
|
||||
|
||||
public String getFullClassName(){
|
||||
return packageName + "." + className;
|
||||
}
|
||||
|
||||
public String getClassName(){
|
||||
return className;
|
||||
}
|
||||
|
||||
public String getPackageName(){
|
||||
String ret = new String(importName.trim());
|
||||
if(ret.startsWith("import "))
|
||||
ret = ret.substring(7);
|
||||
if(ret.endsWith(";"))
|
||||
ret = ret.substring(0, ret.length() - 1).trim();
|
||||
if(ret.endsWith(".*"))
|
||||
ret = ret.substring(0, ret.length() - 2);
|
||||
return ret;
|
||||
}
|
||||
|
||||
public int getTab() {
|
||||
return tab;
|
||||
}
|
||||
|
||||
public int getLineNumber() {
|
||||
return lineNumber;
|
||||
return packageName;
|
||||
}
|
||||
|
||||
public boolean isStarredImport() {
|
||||
String ret = importName.trim();
|
||||
if(ret.endsWith(";")) {
|
||||
ret = ret.substring(0, ret.length() - 1).trim();
|
||||
}
|
||||
return ret.endsWith(".*");
|
||||
return isStarred;
|
||||
}
|
||||
|
||||
public String getImportedClassName() {
|
||||
if (isStarredImport()) {
|
||||
return null;
|
||||
} else {
|
||||
String ret = importName.trim();
|
||||
if(ret.endsWith(";")) {
|
||||
ret = ret.substring(0, ret.length() - 1).trim();
|
||||
}
|
||||
if (ret.lastIndexOf('.') != -1) {
|
||||
return ret.substring(ret.lastIndexOf('.') + 1);
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
public boolean isStaticImport() {
|
||||
return isStatic;
|
||||
}
|
||||
|
||||
public boolean isSameAs(ImportStatement is) {
|
||||
return packageName.equals(is.packageName) &&
|
||||
className.equals(is.className) &&
|
||||
isStatic == is.isStatic;
|
||||
}
|
||||
}
|
||||
293
java/src/processing/mode/java/pdex/SourceMapping.java
Normal file
293
java/src/processing/mode/java/pdex/SourceMapping.java
Normal file
@@ -0,0 +1,293 @@
|
||||
package processing.mode.java.pdex;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.List;
|
||||
import java.util.ListIterator;
|
||||
|
||||
import processing.app.Base;
|
||||
import processing.core.PApplet;
|
||||
|
||||
import static java.awt.SystemColor.text;
|
||||
|
||||
|
||||
public class SourceMapping {
|
||||
|
||||
private static final Comparator<Edit> INPUT_OFFSET_COMP = new Comparator<Edit>() {
|
||||
@Override
|
||||
public int compare(Edit o1, Edit o2) {
|
||||
return Integer.compare(o1.fromOffset, o2.fromOffset);
|
||||
}
|
||||
};
|
||||
|
||||
private static final Comparator<Edit> OUTPUT_OFFSET_COMP = new Comparator<Edit>() {
|
||||
@Override
|
||||
public int compare(Edit o1, Edit o2) {
|
||||
return Integer.compare(o1.toOffset, o2.toOffset);
|
||||
}
|
||||
};
|
||||
|
||||
private static final Comparator<Edit> INPUT_OFFSET_COMP_LEX = new Comparator<Edit>() {
|
||||
@Override
|
||||
public int compare(Edit o1, Edit o2) {
|
||||
int off = Integer.compare(o1.fromOffset, o2.fromOffset);
|
||||
return (off != 0)
|
||||
? off
|
||||
: Integer.compare(o1.fromLength, o2.fromLength);
|
||||
}
|
||||
};
|
||||
|
||||
private static final Comparator<Edit> OUTPUT_OFFSET_COMP_LEX = new Comparator<Edit>() {
|
||||
@Override
|
||||
public int compare(Edit o1, Edit o2) {
|
||||
int off = Integer.compare(o1.toOffset, o2.toOffset);
|
||||
return (off != 0)
|
||||
? off
|
||||
: Integer.compare(o1.toLength, o2.toLength);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
private boolean applied;
|
||||
|
||||
private List<Edit> edits = new ArrayList<>();
|
||||
|
||||
private List<Edit> inMap = new ArrayList<>();
|
||||
private List<Edit> outMap = new ArrayList<>();
|
||||
|
||||
|
||||
public void add(Edit edit) {
|
||||
edits.add(edit);
|
||||
}
|
||||
|
||||
|
||||
public void addAll(Collection<Edit> edits) {
|
||||
this.edits.addAll(edits);
|
||||
}
|
||||
|
||||
|
||||
public void insert(int offset, String text) {
|
||||
edits.add(Edit.insert(offset, text));
|
||||
}
|
||||
|
||||
|
||||
public void delete(int offset, int length) {
|
||||
edits.add(Edit.delete(offset, length));
|
||||
}
|
||||
|
||||
|
||||
public void replace(int offset, int length, String text) {
|
||||
edits.add(Edit.replace(offset, length, text));
|
||||
}
|
||||
|
||||
|
||||
public void move(int fromOffset, int length, int toOffset) {
|
||||
edits.add(Edit.move(fromOffset, length, toOffset));
|
||||
}
|
||||
|
||||
public String apply(CharSequence input) {
|
||||
|
||||
final int inLength = input.length();
|
||||
final int editCount = edits.size();
|
||||
final StringBuilder output = new StringBuilder(inLength);
|
||||
|
||||
// Make copies of Edits to preserve original edits
|
||||
List<Edit> inEdits = new ArrayList<>();
|
||||
for (Edit edit : edits) {
|
||||
inEdits.add(new Edit(edit));
|
||||
}
|
||||
List<Edit> outEdits = new ArrayList<>(inEdits);
|
||||
|
||||
// Edits sorted by input offsets
|
||||
Collections.sort(inEdits, INPUT_OFFSET_COMP);
|
||||
|
||||
// Edits sorted by output offsets
|
||||
Collections.sort(outEdits, OUTPUT_OFFSET_COMP);
|
||||
|
||||
// Check input edits for overlaps
|
||||
for (int i = 0, lastEnd = 0; i < editCount; i++) {
|
||||
Edit ch = inEdits.get(i);
|
||||
if (ch.fromOffset < lastEnd) {
|
||||
String error = "Edits overlap in input! " + inEdits.get(i - 1) + ", " + ch;
|
||||
throw new IndexOutOfBoundsException(error);
|
||||
}
|
||||
lastEnd = ch.fromOffset + ch.fromLength;
|
||||
}
|
||||
|
||||
// Input
|
||||
ListIterator<Edit> inIt = inEdits.listIterator();
|
||||
Edit inEdit = inIt.hasNext() ? inIt.next() : null;
|
||||
int inEditOff = inEdit == null ? input.length() : inEdit.fromOffset;
|
||||
|
||||
// Output
|
||||
ListIterator<Edit> outIt = outEdits.listIterator();
|
||||
Edit outEdit = outIt.hasNext() ? outIt.next() : null;
|
||||
int outEditOff = outEdit == null ? input.length() : outEdit.toOffset;
|
||||
|
||||
int offset = 0;
|
||||
|
||||
inMap.clear();
|
||||
outMap.clear();
|
||||
|
||||
// Walk through the input, apply changes, create mapping
|
||||
while (offset < inLength || inEdit != null || outEdit != null) {
|
||||
|
||||
{ // Copy the unmodified portion of the input, create mapping for it
|
||||
int nextEditOffset = Math.min(inEditOff, outEditOff);
|
||||
|
||||
{ // Insert move block to have mapping for unmodified portions too
|
||||
int length = nextEditOffset - offset;
|
||||
if (length > 0) {
|
||||
Edit ch = Edit.move(offset, length, output.length());
|
||||
inMap.add(ch);
|
||||
outMap.add(ch);
|
||||
}
|
||||
}
|
||||
|
||||
// Copy the block without changes from the input
|
||||
output.append(input, offset, nextEditOffset);
|
||||
|
||||
// Move offset accordingly
|
||||
offset = nextEditOffset;
|
||||
}
|
||||
|
||||
// Process encountered input edits
|
||||
while (inEdit != null && offset >= inEditOff) {
|
||||
offset += inEdit.fromLength;
|
||||
inMap.add(inEdit);
|
||||
inEdit = inIt.hasNext() ? inIt.next() : null;
|
||||
inEditOff = inEdit != null ? inEdit.fromOffset : inLength;
|
||||
}
|
||||
|
||||
// Process encountered output edits
|
||||
while (outEdit != null && offset >= outEditOff) {
|
||||
outEdit.toOffset = output.length();
|
||||
outMap.add(outEdit);
|
||||
if (outEdit.toLength > 0) {
|
||||
if (outEdit.outputText != null) {
|
||||
output.append(outEdit.outputText);
|
||||
} else {
|
||||
output.append(input, outEdit.fromOffset, outEdit.fromOffset + outEdit.fromLength);
|
||||
}
|
||||
}
|
||||
outEdit = outIt.hasNext() ? outIt.next() : null;
|
||||
outEditOff = outEdit != null ? outEdit.toOffset : inLength;
|
||||
}
|
||||
}
|
||||
|
||||
applied = true;
|
||||
|
||||
return output.toString();
|
||||
}
|
||||
|
||||
|
||||
public int getInputOffset(int outputOffset) {
|
||||
if (Base.DEBUG) checkApplied();
|
||||
Edit searchKey = new Edit(0, 0, outputOffset, Integer.MAX_VALUE, null);
|
||||
int i = Collections.binarySearch(outMap, searchKey, OUTPUT_OFFSET_COMP);
|
||||
if (i < 0) {
|
||||
i = -(i + 1);
|
||||
}
|
||||
i = PApplet.constrain(i-1, 0, outMap.size()-1);
|
||||
Edit edit = outMap.get(i);
|
||||
int diff = outputOffset - edit.toOffset;
|
||||
return edit.fromOffset + Math.min(diff, Math.max(0, edit.fromLength - 1));
|
||||
}
|
||||
|
||||
|
||||
public int getOutputOffset(int inputOffset) {
|
||||
if (Base.DEBUG) checkApplied();
|
||||
Edit searchKey = new Edit(inputOffset, Integer.MAX_VALUE, 0, 0, null);
|
||||
int i = Collections.binarySearch(inMap, searchKey, INPUT_OFFSET_COMP);
|
||||
if (i < 0) {
|
||||
i = -(i + 1);
|
||||
}
|
||||
i = PApplet.constrain(i-1, 0, inMap.size()-1);
|
||||
Edit edit = inMap.get(i);
|
||||
int diff = inputOffset - edit.fromOffset;
|
||||
return edit.toOffset + Math.min(diff, Math.max(0, edit.toLength - 1));
|
||||
}
|
||||
|
||||
|
||||
public void clear() {
|
||||
applied = false;
|
||||
edits.clear();
|
||||
inMap.clear();
|
||||
outMap.clear();
|
||||
}
|
||||
|
||||
|
||||
private void checkNotApplied() {
|
||||
if (applied) throw new RuntimeException("this mapping was already applied");
|
||||
}
|
||||
|
||||
|
||||
private void checkApplied() {
|
||||
if (applied) throw new RuntimeException("this mapping was not applied yet");
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "SourceMapping{" +
|
||||
"edits=" + edits +
|
||||
", applied=" + applied +
|
||||
'}';
|
||||
}
|
||||
|
||||
|
||||
protected static class Edit {
|
||||
|
||||
static Edit insert(int offset, String text) {
|
||||
return new Edit(offset, 0, offset, text.length(), text);
|
||||
}
|
||||
|
||||
static Edit replace(int offset, int length, String text) {
|
||||
return new Edit(offset, length, offset, text.length(), text);
|
||||
}
|
||||
|
||||
static Edit move(int fromOffset, int length, int toOffset) {
|
||||
Edit result = new Edit(fromOffset, length, toOffset, length, null);
|
||||
result.toOffset = toOffset;
|
||||
return result;
|
||||
}
|
||||
|
||||
static Edit delete(int position, int length) {
|
||||
return new Edit(position, length, position, 0, null);
|
||||
}
|
||||
|
||||
Edit(Edit edit) {
|
||||
this.fromOffset = edit.fromOffset;
|
||||
this.fromLength = edit.fromLength;
|
||||
this.toOffset = edit.toOffset;
|
||||
this.toLength = edit.toLength;
|
||||
this.outputText = edit.outputText;
|
||||
}
|
||||
|
||||
Edit(int fromOffset, int fromLength, int toOffset, int toLength, String text) {
|
||||
this.fromOffset = fromOffset;
|
||||
this.fromLength = fromLength;
|
||||
this.toOffset = toOffset;
|
||||
this.toLength = toLength;
|
||||
this.outputText = text;
|
||||
}
|
||||
|
||||
final int fromOffset;
|
||||
final int fromLength;
|
||||
int toOffset;
|
||||
final int toLength;
|
||||
final String outputText;
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "Edit{" +
|
||||
"from=" + fromOffset + ":" + fromLength +
|
||||
", to=" + toOffset + ":" + toLength +
|
||||
((text != null) ? (", text='" + outputText + '\'') : "") +
|
||||
'}';
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
297
java/src/processing/mode/java/pdex/SourceUtils.java
Normal file
297
java/src/processing/mode/java/pdex/SourceUtils.java
Normal file
@@ -0,0 +1,297 @@
|
||||
package processing.mode.java.pdex;
|
||||
|
||||
import org.eclipse.jdt.core.dom.AST;
|
||||
import org.eclipse.jdt.core.dom.ASTParser;
|
||||
import org.eclipse.jdt.core.dom.ASTVisitor;
|
||||
import org.eclipse.jdt.core.dom.CompilationUnit;
|
||||
import org.eclipse.jdt.core.dom.MethodDeclaration;
|
||||
import org.eclipse.jdt.core.dom.NumberLiteral;
|
||||
import org.eclipse.jdt.core.dom.SimpleType;
|
||||
import org.eclipse.jdt.core.dom.TypeDeclaration;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import processing.mode.java.pdex.SourceMapping.Edit;
|
||||
import processing.mode.java.preproc.PdePreprocessor;
|
||||
|
||||
public class SourceUtils {
|
||||
|
||||
|
||||
public static final Pattern IMPORT_REGEX =
|
||||
Pattern.compile("(?:^|;)\\s*(import\\s+(?:(static)\\s+)?((?:\\w+\\s*\\.)*)\\s*(\\S+)\\s*;)",
|
||||
Pattern.MULTILINE | Pattern.DOTALL);
|
||||
|
||||
public static final Pattern IMPORT_REGEX_NO_KEYWORD =
|
||||
Pattern.compile("^\\s*((?:(static)\\s+)?((?:\\w+\\s*\\.)*)\\s*(\\S+))",
|
||||
Pattern.MULTILINE | Pattern.DOTALL);
|
||||
|
||||
public static List<Edit> parseProgramImports(CharSequence source,
|
||||
List<ImportStatement> outImports) {
|
||||
|
||||
List<Edit> result = new ArrayList<>();
|
||||
|
||||
Matcher matcher = IMPORT_REGEX.matcher(source);
|
||||
while (matcher.find()) {
|
||||
String piece = matcher.group(1);
|
||||
ImportStatement is = ImportStatement.parse(matcher.toMatchResult());
|
||||
outImports.add(is);
|
||||
int len = piece.length();
|
||||
int idx = matcher.start(1);
|
||||
// Remove the import from the main program
|
||||
// Substitute with white spaces
|
||||
result.add(Edit.move(idx, len, 0));
|
||||
result.add(Edit.insert(0, "\n"));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
|
||||
public static final Pattern TYPE_CONSTRUCTOR_REGEX =
|
||||
Pattern.compile("(?:^|\\W)(int|char|float|boolean|byte)(?:\\s*\\()",
|
||||
Pattern.MULTILINE);
|
||||
|
||||
public static List<Edit> replaceTypeConstructors(CharSequence source) {
|
||||
|
||||
List<Edit> result = new ArrayList<>();
|
||||
|
||||
Matcher matcher = TYPE_CONSTRUCTOR_REGEX.matcher(source);
|
||||
while (matcher.find()) {
|
||||
String match = matcher.group(1);
|
||||
int offset = matcher.start(1);
|
||||
int length = match.length();
|
||||
String replace = "PApplet.parse"
|
||||
+ Character.toUpperCase(match.charAt(0))
|
||||
+ match.substring(1);
|
||||
result.add(Edit.replace(offset, length, replace));
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
|
||||
public static final Pattern HEX_LITERAL_REGEX =
|
||||
Pattern.compile("(?:^|\\W)(#[A-Fa-f0-9]{6})(?:\\W|$)");
|
||||
|
||||
public static List<Edit> replaceHexLiterals(CharSequence source) {
|
||||
// Find all #[webcolor] and replace with 0xff[webcolor]
|
||||
// Should be 6 digits only.
|
||||
List<Edit> result = new ArrayList<>();
|
||||
|
||||
Matcher matcher = HEX_LITERAL_REGEX.matcher(source);
|
||||
while (matcher.find()) {
|
||||
int offset = matcher.start(1);
|
||||
result.add(Edit.replace(offset, 1, "0xff"));
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
|
||||
public static List<Edit> insertImports(List<ImportStatement> imports) {
|
||||
List<Edit> result = new ArrayList<>();
|
||||
for (ImportStatement imp : imports) {
|
||||
result.add(Edit.insert(0, imp.getFullSourceLine() + "\n"));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
public static List<Edit> wrapSketch(PdePreprocessor.Mode mode, String className, int sourceLength) {
|
||||
|
||||
List<Edit> edits = new ArrayList<>();
|
||||
|
||||
StringBuilder b = new StringBuilder();
|
||||
|
||||
// Header
|
||||
if (mode != PdePreprocessor.Mode.JAVA) {
|
||||
b.append("\npublic class ").append(className).append(" extends PApplet {\n");
|
||||
if (mode == PdePreprocessor.Mode.STATIC) {
|
||||
b.append("public void setup() {\n");
|
||||
}
|
||||
}
|
||||
|
||||
edits.add(Edit.insert(0, b.toString()));
|
||||
|
||||
// Reset builder
|
||||
b.setLength(0);
|
||||
|
||||
// Footer
|
||||
if (mode != PdePreprocessor.Mode.JAVA) {
|
||||
if (mode == PdePreprocessor.Mode.STATIC) {
|
||||
// no noLoop() here so it does not tell you
|
||||
// "can't invoke noLoop() on obj" when you type "obj."
|
||||
b.append("\n}");
|
||||
}
|
||||
b.append("\n}\n");
|
||||
}
|
||||
|
||||
edits.add(Edit.insert(sourceLength, b.toString()));
|
||||
|
||||
return edits;
|
||||
}
|
||||
|
||||
|
||||
public static List<Edit> addPublicToTopLeveMethods(CompilationUnit cu) {
|
||||
List<Edit> edits = new ArrayList<>();
|
||||
|
||||
// Add public modifier to top level methods
|
||||
for (Object node : cu.types()) {
|
||||
if (node instanceof TypeDeclaration) {
|
||||
TypeDeclaration type = (TypeDeclaration) node;
|
||||
for (MethodDeclaration method : type.getMethods()) {
|
||||
if (method.modifiers().isEmpty() && !method.isConstructor()) {
|
||||
edits.add(Edit.insert(method.getStartPosition(), "public "));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return edits;
|
||||
}
|
||||
|
||||
public static List<Edit> replaceColorAndFixFloats(CompilationUnit cu) {
|
||||
final List<Edit> edits = new ArrayList<>();
|
||||
|
||||
// Walk the tree, replace "color" with "int" and add 'f' to floats
|
||||
cu.accept(new ASTVisitor() {
|
||||
@Override
|
||||
public boolean visit(SimpleType node) {
|
||||
if ("color".equals(node.getName().toString())) {
|
||||
edits.add(Edit.replace(node.getStartPosition(), node.getLength(), "int"));
|
||||
}
|
||||
return super.visit(node);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean visit(NumberLiteral node) {
|
||||
String s = node.getToken().toLowerCase();
|
||||
if (!s.endsWith("f") && !s.endsWith("d") && (s.contains(".") || s.contains("e"))) {
|
||||
edits.add(Edit.insert(node.getStartPosition() + node.getLength(), "f"));
|
||||
}
|
||||
return super.visit(node);
|
||||
}
|
||||
});
|
||||
|
||||
return edits;
|
||||
}
|
||||
|
||||
/**
|
||||
* Replaces non-ascii characters with their unicode escape sequences and
|
||||
* stuff. Used as it is from
|
||||
* processing.src.processing.mode.java.preproc.PdePreprocessor
|
||||
*
|
||||
* @param program
|
||||
* - Input String containing non ascii characters
|
||||
* @return String - Converted String
|
||||
*/
|
||||
public static String substituteUnicode(String program) {
|
||||
StringBuilder sb = new StringBuilder(program);
|
||||
substituteUnicode(sb);
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
public static void substituteUnicode(StringBuilder p) {
|
||||
// check for non-ascii chars (these will be/must be in unicode format)
|
||||
int unicodeCount = 0;
|
||||
for (int i = 0; i < p.length(); i++) {
|
||||
if (p.charAt(i) > 127) {
|
||||
unicodeCount++;
|
||||
}
|
||||
}
|
||||
if (unicodeCount == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
StringBuilder p2 = new StringBuilder(p.length() + 4 * unicodeCount);
|
||||
|
||||
// if non-ascii chars are in there, convert to unicode escapes
|
||||
// add unicodeCount * 5.. replacing each unicode char
|
||||
// with six digit uXXXX sequence (xxxx is in hex)
|
||||
// (except for nbsp chars which will be a replaced with a space)
|
||||
for (int i = 0; i < p.length(); i++) {
|
||||
int c = p.charAt(i);
|
||||
if (c < 128) {
|
||||
p2.append(c);
|
||||
} else if (c == 160) { // unicode for non-breaking space
|
||||
p2.append(' ');
|
||||
} else if (c >= 128){
|
||||
p2.append("\\u").append(String.format("%04X", c));
|
||||
}
|
||||
}
|
||||
|
||||
p.setLength(0);
|
||||
p.append(p2);
|
||||
}
|
||||
|
||||
|
||||
static public String scrubComments(String p) {
|
||||
StringBuilder sb = new StringBuilder(p);
|
||||
scrubComments(sb);
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Replace all commented portions of a given String as spaces.
|
||||
* Utility function used here and in the preprocessor.
|
||||
*/
|
||||
static public void scrubComments(StringBuilder p) {
|
||||
// Track quotes to avoid problems with code like: String t = "*/*";
|
||||
// http://code.google.com/p/processing/issues/detail?id=1435
|
||||
boolean insideQuote = false;
|
||||
|
||||
int length = p.length();
|
||||
|
||||
int index = 0;
|
||||
while (index < length) {
|
||||
// for any double slash comments, ignore until the end of the line
|
||||
if (!insideQuote &&
|
||||
(p.charAt(index) == '/') &&
|
||||
(index < length - 1) &&
|
||||
(p.charAt(index+1) == '/')) {
|
||||
p.setCharAt(index++, ' ');
|
||||
p.setCharAt(index++, ' ');
|
||||
while ((index < length) &&
|
||||
(p.charAt(index) != '\n')) {
|
||||
p.setCharAt(index++, ' ');
|
||||
}
|
||||
|
||||
// check to see if this is the start of a new multiline comment.
|
||||
// if it is, then make sure it's actually terminated somewhere.
|
||||
} else if (!insideQuote &&
|
||||
(p.charAt(index) == '/') &&
|
||||
(index < length - 1) &&
|
||||
(p.charAt(index+1) == '*')) {
|
||||
p.setCharAt(index++, ' ');
|
||||
p.setCharAt(index++, ' ');
|
||||
boolean endOfRainbow = false;
|
||||
while (index < length - 1) {
|
||||
if ((p.charAt(index) == '*') && (p.charAt(index+1) == '/')) {
|
||||
p.setCharAt(index++, ' ');
|
||||
p.setCharAt(index++, ' ');
|
||||
endOfRainbow = true;
|
||||
break;
|
||||
|
||||
} else {
|
||||
// continue blanking this area
|
||||
p.setCharAt(index++, ' ');
|
||||
}
|
||||
}
|
||||
if (!endOfRainbow) {
|
||||
throw new RuntimeException("Missing the */ from the end of a " +
|
||||
"/* comment */");
|
||||
}
|
||||
} else if (p.charAt(index) == '"' && index > 0 && p.charAt(index-1) != '\\') {
|
||||
insideQuote = !insideQuote;
|
||||
index++;
|
||||
|
||||
} else { // any old character, move along
|
||||
index++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,215 +0,0 @@
|
||||
/* -*- mode: java; c-basic-offset: 2; indent-tabs-mode: nil -*- */
|
||||
|
||||
/*
|
||||
Part of the Processing project - http://processing.org
|
||||
Copyright (c) 2012-15 The Processing Foundation
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License version 2
|
||||
as published by the Free Software Foundation.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software Foundation, Inc.
|
||||
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
package processing.mode.java.pdex;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.eclipse.jdt.core.dom.AST;
|
||||
import org.eclipse.jdt.core.dom.ASTParser;
|
||||
import org.eclipse.jdt.core.dom.ASTVisitor;
|
||||
import org.eclipse.jdt.core.dom.CompilationUnit;
|
||||
import org.eclipse.jdt.core.dom.MethodDeclaration;
|
||||
import org.eclipse.jdt.core.dom.Modifier;
|
||||
import org.eclipse.jdt.core.dom.NumberLiteral;
|
||||
import org.eclipse.jdt.core.dom.rewrite.ASTRewrite;
|
||||
import org.eclipse.jface.text.BadLocationException;
|
||||
import org.eclipse.jface.text.Document;
|
||||
import org.eclipse.text.edits.MalformedTreeException;
|
||||
import org.eclipse.text.edits.TextEdit;
|
||||
|
||||
import processing.app.Util;
|
||||
import processing.data.StringList;
|
||||
import processing.mode.java.JavaEditor;
|
||||
import processing.mode.java.preproc.PdePreprocessor;
|
||||
|
||||
|
||||
/**
|
||||
* Implementation of PdePreprocessor that uses Eclipse JDT features
|
||||
* instead of ANTLR.
|
||||
*/
|
||||
public class XQPreprocessor {
|
||||
private ASTRewrite rewrite = null;
|
||||
private List<ImportStatement> extraImports;
|
||||
private final JavaEditor editor;
|
||||
private String[] coreImports;
|
||||
private String[] defaultImports;
|
||||
|
||||
|
||||
public XQPreprocessor(JavaEditor editor) {
|
||||
this.editor = editor;
|
||||
|
||||
// get parameters from the main preproc
|
||||
// PdePreprocessor p = new PdePreprocessor(null);
|
||||
PdePreprocessor p = editor.createPreprocessor(null);
|
||||
defaultImports = p.getDefaultImports();
|
||||
coreImports = p.getCoreImports();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* The main preprocessing method that converts code into compilable Java.
|
||||
*/
|
||||
protected String handle(String source,
|
||||
List<ImportStatement> programImports) {
|
||||
this.extraImports = programImports;
|
||||
Document doc = new Document(source);
|
||||
|
||||
ASTParser parser = ASTParser.newParser(AST.JLS8);
|
||||
parser.setSource(doc.get().toCharArray());
|
||||
parser.setKind(ASTParser.K_COMPILATION_UNIT);
|
||||
|
||||
parser.setCompilerOptions(ErrorCheckerService.COMPILER_OPTIONS);
|
||||
CompilationUnit cu = (CompilationUnit) parser.createAST(null);
|
||||
cu.recordModifications();
|
||||
rewrite = ASTRewrite.create(cu.getAST());
|
||||
cu.accept(new XQVisitor());
|
||||
|
||||
TextEdit edits = cu.rewrite(doc, null);
|
||||
try {
|
||||
edits.apply(doc);
|
||||
} catch (MalformedTreeException | BadLocationException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
return doc.get();
|
||||
}
|
||||
|
||||
|
||||
protected String prepareImports(List<ImportStatement> programImports) {
|
||||
this.extraImports = programImports;
|
||||
|
||||
StringList imports = new StringList();
|
||||
for (ImportStatement imp : extraImports) {
|
||||
imports.append(imp.getImportName());
|
||||
}
|
||||
imports.append("// Default Imports");
|
||||
for (String imp : coreImports) {
|
||||
imports.append("import " + imp + ";");
|
||||
}
|
||||
for (String imp : defaultImports) {
|
||||
imports.append("import " + imp + ";");
|
||||
}
|
||||
if (editor.getSketch().getCodeFolder().exists()) {
|
||||
StringList codeFolderPackages = null;
|
||||
String codeFolderClassPath = Util.contentsToClassPath(editor.getSketch().getCodeFolder());
|
||||
codeFolderPackages = Util.packageListFromClassPath(codeFolderClassPath);
|
||||
if (codeFolderPackages != null) {
|
||||
editor.getErrorChecker().codeFolderImports.clear();
|
||||
for (String item : codeFolderPackages) {
|
||||
// Messages.log("CF import " + item);
|
||||
imports.append("import " + item + ".*;");
|
||||
editor.getErrorChecker().codeFolderImports.add(new ImportStatement("import " + item + ".*;",0,0));
|
||||
}
|
||||
}
|
||||
}
|
||||
return imports.join("\n") + "\n";
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Visitor implementation that does all the substitution dirty work. <br>
|
||||
* <LI>Any function not specified as being protected or private will be made
|
||||
* 'public'. This means that <TT>void setup()</TT> becomes
|
||||
* <TT>public void setup()</TT>.
|
||||
*
|
||||
* <LI>Converts doubles into floats, i.e. 12.3 becomes 12.3f so that people
|
||||
* don't have to add f after their numbers all the time since it's confusing
|
||||
* for beginners. Also, most functions of p5 core deal with floats only.
|
||||
*/
|
||||
private class XQVisitor extends ASTVisitor {
|
||||
@SuppressWarnings({ "unchecked", "rawtypes" })
|
||||
public boolean visit(MethodDeclaration node) {
|
||||
if (node.getReturnType2() != null) {
|
||||
// if return type is color, make it int
|
||||
// if (node.getReturnType2().toString().equals("color")) {
|
||||
// System.err.println("color type detected!");
|
||||
// node.setReturnType2(rewrite.getAST().newPrimitiveType(
|
||||
// PrimitiveType.INT));
|
||||
// }
|
||||
|
||||
// The return type is not void, no need to make it public
|
||||
// if (!node.getReturnType2().toString().equals("void"))
|
||||
// return true;
|
||||
}
|
||||
|
||||
// Simple method, make it public
|
||||
if (node.modifiers().size() == 0 && !node.isConstructor()) {
|
||||
// rewrite.set(node, node.getModifiersProperty(),
|
||||
// Modifier.PUBLIC,
|
||||
// null);
|
||||
// rewrite.getListRewrite(node,
|
||||
// node.getModifiersProperty()).insertLast(Modifier., null)
|
||||
List newMod = rewrite.getAST().newModifiers(Modifier.PUBLIC);
|
||||
node.modifiers().add(newMod.get(0));
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
public boolean visit(NumberLiteral node) {
|
||||
// Need to handle both 1.0F and 1.0D cases
|
||||
// https://github.com/processing/processing/issues/3707
|
||||
String lower = node.getToken().toLowerCase();
|
||||
if (!lower.endsWith("f") && !lower.endsWith("d")) {
|
||||
for (int i = 0; i < node.getToken().length(); i++) {
|
||||
if (node.getToken().charAt(i) == '.') {
|
||||
String s = node.getToken() + "f";
|
||||
node.setToken(s);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
// public boolean visit(FieldDeclaration node) {
|
||||
// if (node.getType().toString().equals("color")){
|
||||
// System.err.println("color type detected!");
|
||||
// node.setType(rewrite.getAST().newPrimitiveType(
|
||||
// PrimitiveType.INT));
|
||||
// }
|
||||
// return true;
|
||||
// }
|
||||
//
|
||||
// public boolean visit(VariableDeclarationStatement node) {
|
||||
// if (node.getType().toString().equals("color")){
|
||||
// System.err.println("color type detected!");
|
||||
// node.setType(rewrite.getAST().newPrimitiveType(
|
||||
// PrimitiveType.INT));
|
||||
// }
|
||||
// return true;
|
||||
// }
|
||||
|
||||
|
||||
// /**
|
||||
// * This is added just for debugging purposes - to make sure that all
|
||||
// * instances of color type have been substituded as in by the regex
|
||||
// * search in ErrorCheckerService.preprocessCode().
|
||||
// */
|
||||
// public boolean visit(SimpleType node) {
|
||||
// if (node.toString().equals("color")) {
|
||||
// System.err.println("Color type detected: please report as an issue.");
|
||||
// }
|
||||
// return true;
|
||||
// }
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user