diff --git a/app/src/processing/app/Base.java b/app/src/processing/app/Base.java index 04c486b78..d93b87ff1 100644 --- a/app/src/processing/app/Base.java +++ b/app/src/processing/app/Base.java @@ -31,7 +31,6 @@ import java.awt.event.ActionListener; import java.io.*; import java.text.SimpleDateFormat; import java.util.*; -import java.util.zip.*; import javax.swing.JDialog; import javax.swing.JFileChooser; @@ -43,14 +42,8 @@ import javax.swing.JPopupMenu; import javax.swing.tree.DefaultMutableTreeNode; import processing.app.contrib.*; -import processing.app.ui.Editor; -import processing.app.ui.EditorConsole; -import processing.app.ui.EditorState; -import processing.app.ui.PreferencesFrame; -import processing.app.ui.Recent; -import processing.app.ui.Welcome; +import processing.app.ui.*; import processing.core.*; -import processing.data.StringDict; import processing.data.StringList; @@ -1828,35 +1821,6 @@ public class Base { } -// /** -// * Check for a new sketchbook location. -// */ -// static protected File promptSketchbookLocation() { -// // Most often this will happen on Linux, so default to their home dir. -// File folder = new File(System.getProperty("user.home"), "sketchbook"); -// String prompt = "Select a folder to place sketches..."; -// -//// FolderSelector fs = new FolderSelector(prompt, folder, new Frame()); -//// folder = fs.getFolder(); -// folder = Base.selectFolder(prompt, folder, new Frame()); -// -//// folder = Base.selectFolder(prompt, folder, null); -//// PApplet.selectFolder(prompt, -//// "promptSketchbookCallback", dflt, -//// Preferences.this, dialog); -// -// if (folder == null) { -// System.exit(0); -// } -// // Create the folder if it doesn't exist already -// if (!folder.exists()) { -// folder.mkdirs(); -// return folder; -// } -// return folder; -// } - - // ................................................................. @@ -1907,90 +1871,6 @@ public class Base { // ................................................................. -// /** -// * Prompt for a folder and return it as a File object (or null). -// * Implementation for choosing directories that handles both the -// * Mac OS X hack to allow the native AWT file dialog, or uses -// * the JFileChooser on other platforms. Mac AWT trick obtained from -// * this post -// * on the OS X Java dev archive which explains the cryptic note in -// * Apple's Java 1.4 release docs about the special System property. -// */ -// static public File selectFolder(String prompt, File folder, Frame frame) { -// if (Base.isMacOS()) { -// if (frame == null) frame = new Frame(); //.pack(); -// FileDialog fd = new FileDialog(frame, prompt, FileDialog.LOAD); -// if (folder != null) { -// fd.setDirectory(folder.getParent()); -// //fd.setFile(folder.getName()); -// } -// System.setProperty("apple.awt.fileDialogForDirectories", "true"); -// fd.setVisible(true); -// System.setProperty("apple.awt.fileDialogForDirectories", "false"); -// if (fd.getFile() == null) { -// return null; -// } -// return new File(fd.getDirectory(), fd.getFile()); -// -// } else { -// JFileChooser fc = new JFileChooser(); -// fc.setDialogTitle(prompt); -// if (folder != null) { -// fc.setSelectedFile(folder); -// } -// fc.setFileSelectionMode(JFileChooser.DIRECTORIES_ONLY); -// -// int returned = fc.showOpenDialog(new JDialog()); -// if (returned == JFileChooser.APPROVE_OPTION) { -// return fc.getSelectedFile(); -// } -// } -// return null; -// } - - -// static class FolderSelector { -// File folder; -// boolean ready; -// -// FolderSelector(String prompt, File defaultFile, Frame parentFrame) { -// PApplet.selectFolder(prompt, "callback", defaultFile, this, parentFrame); -// } -// -// public void callback(File folder) { -// this.folder = folder; -// ready = true; -// } -// -// boolean isReady() { -// return ready; -// } -// -// /** block until the folder is available */ -// File getFolder() { -// while (!ready) { -// try { -// Thread.sleep(100); -// } catch (InterruptedException e) { } -// } -// return folder; -// } -// } -// -// -// /** -// * Blocking version of folder selection. Runs and sleeps until an answer -// * comes back. Avoid using: try to make things work with the async -// * selectFolder inside PApplet instead. -// */ -// static public File selectFolder(String prompt, File folder, Frame frame) { -// return new FolderSelector(prompt, folder, frame).getFolder(); -// } - - - // ................................................................. - - /** * "No cookie for you" type messages. Nothing fatal or all that * much of a bummer, but something to notify the user about. @@ -2355,588 +2235,6 @@ public class Base { // . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . - /** - * Get the number of lines in a file by counting the number of newline - * characters inside a String (and adding 1). - */ - static public int countLines(String what) { - int count = 1; - for (char c : what.toCharArray()) { - if (c == '\n') count++; - } - return count; - } - - - /** - * Same as PApplet.loadBytes(), however never does gzip decoding. - */ - static public byte[] loadBytesRaw(File file) throws IOException { - int size = (int) file.length(); - FileInputStream input = new FileInputStream(file); - byte buffer[] = new byte[size]; - int offset = 0; - int bytesRead; - while ((bytesRead = input.read(buffer, offset, size-offset)) != -1) { - offset += bytesRead; - if (bytesRead == 0) break; - } - input.close(); // weren't properly being closed - input = null; - return buffer; - } - - - /** - * Read from a file with a bunch of attribute/value pairs - * that are separated by = and ignore comments with #. - * Changed in 3.0a6 to return null (rather than empty hash) if no file, - * and changed return type to Map instead of HashMap. - */ - static public StringDict readSettings(File inputFile) { - if (!inputFile.exists()) { - if (DEBUG) System.err.println(inputFile + " does not exist."); - return null; - } - String lines[] = PApplet.loadStrings(inputFile); - if (lines == null) { - System.err.println("Could not read " + inputFile); - return null; - } - return readSettings(inputFile.toString(), lines); - } - - - /** - * Parse a String array that contains attribute/value pairs separated - * by = (the equals sign). The # (hash) symbol is used to denote comments. - * Comments can be anywhere on a line. Blank lines are ignored. - * In 3.0a6, no longer taking a blank HashMap as param; no cases in the main - * PDE code of adding to a (Hash)Map. Also returning the Map instead of void. - * Both changes modify the method signature, but this was only used by the - * contrib classes. - */ - static public StringDict readSettings(String filename, String[] lines) { - StringDict settings = new StringDict(); - for (String line : lines) { - // Remove comments - int commentMarker = line.indexOf('#'); - if (commentMarker != -1) { - line = line.substring(0, commentMarker); - } - // Remove extra whitespace - line = line.trim(); - - if (line.length() != 0) { - int equals = line.indexOf('='); - if (equals == -1) { - if (filename != null) { - System.err.println("Ignoring illegal line in " + filename); - System.err.println(" " + line); - } - } else { - String attr = line.substring(0, equals).trim(); - String valu = line.substring(equals + 1).trim(); - settings.set(attr, valu); - } - } - } - return settings; - } - - - static public void copyFile(File sourceFile, - File targetFile) throws IOException { - BufferedInputStream from = - new BufferedInputStream(new FileInputStream(sourceFile)); - BufferedOutputStream to = - new BufferedOutputStream(new FileOutputStream(targetFile)); - byte[] buffer = new byte[16 * 1024]; - int bytesRead; - while ((bytesRead = from.read(buffer)) != -1) { - to.write(buffer, 0, bytesRead); - } - from.close(); - from = null; - - to.flush(); - to.close(); - to = null; - - targetFile.setLastModified(sourceFile.lastModified()); - targetFile.setExecutable(sourceFile.canExecute()); - } - - - /** - * Grab the contents of a file as a string. - */ - static public String loadFile(File file) throws IOException { - String[] contents = PApplet.loadStrings(file); - if (contents == null) return null; - return PApplet.join(contents, "\n"); - } - - - /** - * Spew the contents of a String object out to a file. - */ - static public void saveFile(String str, File file) throws IOException { - File temp = File.createTempFile(file.getName(), null, file.getParentFile()); - try { - // fix from cjwant to prevent symlinks from being destroyed. - File canon = file.getCanonicalFile(); - // assign the var as second step since previous line may throw exception - file = canon; - } catch (IOException e) { - throw new IOException("Could not resolve canonical representation of " + - file.getAbsolutePath()); - } - // Can't use saveStrings() here b/c Windows will add a ^M to the file - PrintWriter writer = PApplet.createWriter(temp); - writer.print(str); - boolean error = writer.checkError(); // calls flush() - writer.close(); // attempt to close regardless - if (error) { - throw new IOException("Error while trying to save " + file); - } - - // remove the old file before renaming the temp file - if (file.exists()) { - boolean result = file.delete(); - if (!result) { - throw new IOException("Could not remove old version of " + - file.getAbsolutePath()); - } - } - boolean result = temp.renameTo(file); - if (!result) { - throw new IOException("Could not replace " + file.getAbsolutePath() + - " with " + temp.getAbsolutePath()); - } - } - - - /** - * Copy a folder from one place to another. This ignores all dot files and - * folders found in the source directory, to avoid copying silly .DS_Store - * files and potentially troublesome .svn folders. - */ - static public void copyDir(File sourceDir, - File targetDir) throws IOException { - if (sourceDir.equals(targetDir)) { - final String urDum = "source and target directories are identical"; - throw new IllegalArgumentException(urDum); - } - targetDir.mkdirs(); - String files[] = sourceDir.list(); - for (int i = 0; i < files.length; i++) { - // Ignore dot files (.DS_Store), dot folders (.svn) while copying - if (files[i].charAt(0) == '.') continue; - //if (files[i].equals(".") || files[i].equals("..")) continue; - File source = new File(sourceDir, files[i]); - File target = new File(targetDir, files[i]); - if (source.isDirectory()) { - //target.mkdirs(); - copyDir(source, target); - target.setLastModified(source.lastModified()); - } else { - copyFile(source, target); - } - } - } - - - static public void copyDirNative(File sourceDir, - File targetDir) throws IOException { - Process process = null; - if (Base.isMacOS() || Base.isLinux()) { - process = Runtime.getRuntime().exec(new String[] { - "cp", "-a", sourceDir.getAbsolutePath(), targetDir.getAbsolutePath() - }); - } else { - // TODO implement version that uses XCOPY here on Windows - throw new RuntimeException("Not yet implemented on Windows"); - } - try { - int result = process.waitFor(); - if (result != 0) { - throw new IOException("Error while copying (result " + result + ")"); - } - } catch (InterruptedException e) { - e.printStackTrace(); - } - } - - - /** - * Delete a file or directory in a platform-specific manner. Removes a File - * object (a file or directory) from the system by placing it in the Trash - * or Recycle Bin (if available) or simply deleting it (if not). - * - * When the file/folder is on another file system, it may simply be removed - * immediately, without additional warning. So only use this if you want to, - * you know, "delete" the subject in question. - * - * NOTE: Not yet tested nor ready for prime-time. - * - * @param file the victim (a directory or individual file) - * @return true if all ends well - * @throws IOException what went wrong - */ - static public boolean platformDelete(File file) throws IOException { - return platform.deleteFile(file); - } - - - /** - * Remove all files in a directory and the directory itself. - */ - static public void removeDir(File dir) { - if (dir.exists()) { - removeDescendants(dir); - if (!dir.delete()) { - System.err.println("Could not delete " + dir); - } - } - } - - - /** - * Recursively remove all files within a directory, - * used with removeDir(), or when the contents of a dir - * should be removed, but not the directory itself. - * (i.e. when cleaning temp files from lib/build) - */ - static public void removeDescendants(File dir) { - if (!dir.exists()) return; - - String files[] = dir.list(); - for (int i = 0; i < files.length; i++) { - if (files[i].equals(".") || files[i].equals("..")) continue; - File dead = new File(dir, files[i]); - if (!dead.isDirectory()) { - if (!Preferences.getBoolean("compiler.save_build_files")) { - if (!dead.delete()) { - // temporarily disabled - System.err.println("Could not delete " + dead); - } - } - } else { - removeDir(dead); - //dead.delete(); - } - } - } - - - /** - * Calculate the size of the contents of a folder. - * Used to determine whether sketches are empty or not. - * Note that the function calls itself recursively. - */ - static public int calcFolderSize(File folder) { - int size = 0; - - String files[] = folder.list(); - // null if folder doesn't exist, happens when deleting sketch - if (files == null) return -1; - - for (int i = 0; i < files.length; i++) { - if (files[i].equals(".") || - files[i].equals("..") || - files[i].equals(".DS_Store")) continue; - File fella = new File(folder, files[i]); - if (fella.isDirectory()) { - size += calcFolderSize(fella); - } else { - size += (int) fella.length(); - } - } - return size; - } - - - /** - * Recursively creates a list of all files within the specified folder, - * and returns a list of their relative paths. - * Ignores any files/folders prefixed with a dot. - */ -// static public String[] listFiles(String path, boolean relative) { -// return listFiles(new File(path), relative); -// } - - - static public String[] listFiles(File folder, boolean relative) { - String path = folder.getAbsolutePath(); - Vector vector = new Vector(); - listFiles(relative ? (path + File.separator) : "", path, null, vector); - String outgoing[] = new String[vector.size()]; - vector.copyInto(outgoing); - return outgoing; - } - - - static public String[] listFiles(File folder, boolean relative, String extension) { - String path = folder.getAbsolutePath(); - Vector vector = new Vector(); - if (extension != null) { - if (!extension.startsWith(".")) { - extension = "." + extension; - } - } - listFiles(relative ? (path + File.separator) : "", path, extension, vector); - String outgoing[] = new String[vector.size()]; - vector.copyInto(outgoing); - return outgoing; - } - - - static protected void listFiles(String basePath, - String path, String extension, - Vector vector) { - File folder = new File(path); - String[] list = folder.list(); - if (list != null) { - for (String item : list) { - if (item.charAt(0) == '.') continue; - if (extension == null || item.toLowerCase().endsWith(extension)) { - File file = new File(path, item); - String newPath = file.getAbsolutePath(); - if (newPath.startsWith(basePath)) { - newPath = newPath.substring(basePath.length()); - } - // only add if no ext or match - if (extension == null || item.toLowerCase().endsWith(extension)) { - vector.add(newPath); - } - if (file.isDirectory()) { // use absolute path - listFiles(basePath, file.getAbsolutePath(), extension, vector); - } - } - } - } - } - - - /** - * @param folder source folder to search - * @return a list of .jar and .zip files in that folder - */ - static public File[] listJarFiles(File folder) { - return folder.listFiles(new FilenameFilter() { - public boolean accept(File dir, String name) { - return (!name.startsWith(".") && - (name.toLowerCase().endsWith(".jar") || - name.toLowerCase().endsWith(".zip"))); - } - }); - } - - - ///////////////////////////////////////////////////////////////////////////// - - - /** - * Given a folder, return a list of absolute paths to all jar or zip files - * inside that folder, separated by pathSeparatorChar. - * - * This will prepend a colon (or whatever the path separator is) - * so that it can be directly appended to another path string. - * - * As of 0136, this will no longer add the root folder as well. - * - * This function doesn't bother checking to see if there are any .class - * files in the folder or within a subfolder. - */ - static public String contentsToClassPath(File folder) { - if (folder == null) return ""; - - StringBuilder sb = new StringBuilder(); - String sep = System.getProperty("path.separator"); - - try { - String path = folder.getCanonicalPath(); - - // When getting the name of this folder, make sure it has a slash - // after it, so that the names of sub-items can be added. - if (!path.endsWith(File.separator)) { - path += File.separator; - } - - String list[] = folder.list(); - for (int i = 0; i < list.length; i++) { - // Skip . and ._ files. Prior to 0125p3, .jar files that had - // OS X AppleDouble files associated would cause trouble. - if (list[i].startsWith(".")) continue; - - if (list[i].toLowerCase().endsWith(".jar") || - list[i].toLowerCase().endsWith(".zip")) { - sb.append(sep); - sb.append(path); - sb.append(list[i]); - } - } - } catch (IOException e) { - e.printStackTrace(); // this would be odd - } - return sb.toString(); - } - - - /** - * A classpath, separated by the path separator, will contain - * a series of .jar/.zip files or directories containing .class - * files, or containing subdirectories that have .class files. - * - * @param path the input classpath - * @return array of possible package names - */ - static public StringList packageListFromClassPath(String path) { -// Map map = new HashMap(); - StringList list = new StringList(); - String pieces[] = - PApplet.split(path, File.pathSeparatorChar); - - for (int i = 0; i < pieces.length; i++) { - //System.out.println("checking piece '" + pieces[i] + "'"); - if (pieces[i].length() == 0) continue; - - if (pieces[i].toLowerCase().endsWith(".jar") || - pieces[i].toLowerCase().endsWith(".zip")) { - //System.out.println("checking " + pieces[i]); - packageListFromZip(pieces[i], list); - - } else { // it's another type of file or directory - File dir = new File(pieces[i]); - if (dir.exists() && dir.isDirectory()) { - packageListFromFolder(dir, null, list); - //importCount = magicImportsRecursive(dir, null, - // map); - //imports, importCount); - } - } - } -// int mapCount = map.size(); -// String output[] = new String[mapCount]; -// int index = 0; -// Set set = map.keySet(); -// for (String s : set) { -// output[index++] = s.replace('/', '.'); -// } -// return output; - StringList outgoing = new StringList(list.size()); - for (String item : list) { - outgoing.append(item.replace('/', '.')); - } - return outgoing; - } - - - static private void packageListFromZip(String filename, StringList list) { - try { - ZipFile file = new ZipFile(filename); - Enumeration entries = file.entries(); - while (entries.hasMoreElements()) { - ZipEntry entry = (ZipEntry) entries.nextElement(); - - if (!entry.isDirectory()) { - String name = entry.getName(); - - if (name.endsWith(".class")) { - int slash = name.lastIndexOf('/'); - if (slash == -1) continue; - - String pname = name.substring(0, slash); -// if (map.get(pname) == null) { -// map.put(pname, new Object()); -// } - list.appendUnique(pname); - } - } - } - file.close(); - } catch (IOException e) { - System.err.println("Ignoring " + filename + " (" + e.getMessage() + ")"); - //e.printStackTrace(); - } - } - - - /** - * Make list of package names by traversing a directory hierarchy. - * Each time a class is found in a folder, add its containing set - * of folders to the package list. If another folder is found, - * walk down into that folder and continue. - */ - static private void packageListFromFolder(File dir, String sofar, - StringList list) { -// Map map) { - boolean foundClass = false; - String files[] = dir.list(); - - for (int i = 0; i < files.length; i++) { - if (files[i].equals(".") || files[i].equals("..")) continue; - - File sub = new File(dir, files[i]); - if (sub.isDirectory()) { - String nowfar = - (sofar == null) ? files[i] : (sofar + "." + files[i]); - packageListFromFolder(sub, nowfar, list); - //System.out.println(nowfar); - //imports[importCount++] = nowfar; - //importCount = magicImportsRecursive(sub, nowfar, - // imports, importCount); - } else if (!foundClass) { // if no classes found in this folder yet - if (files[i].endsWith(".class")) { - //System.out.println("unique class: " + files[i] + " for " + sofar); -// map.put(sofar, new Object()); - list.appendUnique(sofar); - foundClass = true; - } - } - } - } - - - static public void unzip(File zipFile, File dest) { - try { - FileInputStream fis = new FileInputStream(zipFile); - CheckedInputStream checksum = new CheckedInputStream(fis, new Adler32()); - ZipInputStream zis = new ZipInputStream(new BufferedInputStream(checksum)); - ZipEntry next = null; - while ((next = zis.getNextEntry()) != null) { - File currentFile = new File(dest, next.getName()); - if (next.isDirectory()) { - currentFile.mkdirs(); - } else { - File parentDir = currentFile.getParentFile(); - // Sometimes the directory entries aren't already created - if (!parentDir.exists()) { - parentDir.mkdirs(); - } - currentFile.createNewFile(); - unzipEntry(zis, currentFile); - } - } - } catch (Exception e) { - e.printStackTrace(); - } - } - - - static protected void unzipEntry(ZipInputStream zin, File f) throws IOException { - FileOutputStream out = new FileOutputStream(f); - byte[] b = new byte[512]; - int len = 0; - while ((len = zin.read(b)) != -1) { - out.write(b, 0, len); - } - out.flush(); - out.close(); - } - - static public void log(Object from, String message) { if (DEBUG) { System.out.println(from.getClass().getName() + ": " + message); diff --git a/app/src/processing/app/Language.java b/app/src/processing/app/Language.java index b9b290a64..6f096aafa 100644 --- a/app/src/processing/app/Language.java +++ b/app/src/processing/app/Language.java @@ -144,7 +144,7 @@ public class Language { */ static public void saveLanguage(String language) { try { - Base.saveFile(language, prefFile); + Util.saveFile(language, prefFile); } catch (Exception e) { e.printStackTrace(); } diff --git a/app/src/processing/app/Library.java b/app/src/processing/app/Library.java index abfab0445..71e8f161b 100644 --- a/app/src/processing/app/Library.java +++ b/app/src/processing/app/Library.java @@ -116,7 +116,7 @@ public class Library extends LocalContribution { File exportSettings = new File(libraryFolder, "export.txt"); StringDict exportTable = exportSettings.exists() ? - Base.readSettings(exportSettings) : new StringDict(); + Util.readSettings(exportSettings) : new StringDict(); exportList = new HashMap(); @@ -214,7 +214,7 @@ public class Library extends LocalContribution { // } // get the path for all .jar files in this code folder - packageList = Base.packageListFromClassPath(getClassPath()); + packageList = Util.packageListFromClassPath(getClassPath()); } diff --git a/app/src/processing/app/Mode.java b/app/src/processing/app/Mode.java index e46ec7fbf..376ab4a94 100644 --- a/app/src/processing/app/Mode.java +++ b/app/src/processing/app/Mode.java @@ -1399,7 +1399,7 @@ public abstract class Mode { // Nuke the old applet/application folder because it can cause trouble if (Preferences.getBoolean("export.delete_target_folder")) { // System.out.println("temporarily skipping deletion of " + targetFolder); - Base.removeDir(targetFolder); + Util.removeDir(targetFolder); // targetFolder.renameTo(dest); } // Create a fresh output folder (needed before preproc is run next) diff --git a/app/src/processing/app/Platform.java b/app/src/processing/app/Platform.java index 208c08b9b..1f644858c 100644 --- a/app/src/processing/app/Platform.java +++ b/app/src/processing/app/Platform.java @@ -173,7 +173,7 @@ public class Platform { return true; } else if (file.isDirectory()) { - Base.removeDir(file); + Util.removeDir(file); return true; } else { diff --git a/app/src/processing/app/Sketch.java b/app/src/processing/app/Sketch.java index d5c87069b..9b57164ed 100644 --- a/app/src/processing/app/Sketch.java +++ b/app/src/processing/app/Sketch.java @@ -652,7 +652,7 @@ public class Sketch { // to do a save on the handleNew() // delete the entire sketch - Base.removeDir(folder); + Util.removeDir(folder); // get the changes into the sketchbook menu //sketchbook.rebuildMenus(); @@ -898,7 +898,7 @@ public class Sketch { // if the new folder already exists, then first remove its contents before // copying everything over (user will have already been warned). if (newFolder.exists()) { - Base.removeDir(newFolder); + Util.removeDir(newFolder); } // in fact, you can't do this on Windows because the file dialog // will instead put you inside the folder, but it happens on OS X a lot. @@ -1148,7 +1148,7 @@ public class Sketch { // https://github.com/processing/processing/issues/3383 if (!sourceFile.equals(destFile)) { try { - Base.copyFile(sourceFile, destFile); + Util.copyFile(sourceFile, destFile); } catch (IOException e) { Base.showWarning(Language.text("add_file.messages.error_adding"), diff --git a/app/src/processing/app/SketchCode.java b/app/src/processing/app/SketchCode.java index 38399cd04..11e34eb5b 100644 --- a/app/src/processing/app/SketchCode.java +++ b/app/src/processing/app/SketchCode.java @@ -54,7 +54,7 @@ public class SketchCode { /** Last time this tab was visited */ long visited; - + /** The last time this tab was saved to disk */ private long lastModified; @@ -136,7 +136,7 @@ public class SketchCode { public void copyTo(File dest) throws IOException { - Base.saveFile(program, dest); + Util.saveFile(program, dest); } @@ -179,7 +179,7 @@ public class SketchCode { public int getLineCount() { - return Base.countLines(program); + return Util.countLines(program); } @@ -275,7 +275,7 @@ public class SketchCode { * Load this piece of code from a file. */ public void load() throws IOException { - program = Base.loadFile(file); + program = Util.loadFile(file); // Remove NUL characters because they'll cause problems, // and their presence is very difficult to debug. @@ -309,7 +309,7 @@ public class SketchCode { // TODO re-enable history //history.record(s, SketchHistory.SAVE); - Base.saveFile(program, file); + Util.saveFile(program, file); savedProgram = program; lastModified = file.lastModified(); setModified(false); @@ -320,7 +320,7 @@ public class SketchCode { * Save this file to another location, used by Sketch.saveAs() */ public void saveAs(File newFile) throws IOException { - Base.saveFile(program, newFile); + Util.saveFile(program, newFile); savedProgram = program; file = newFile; makePrettyName(); @@ -336,7 +336,7 @@ public class SketchCode { public void setFolder(File sketchFolder) { file = new File(sketchFolder, file.getName()); } - + /** * Used to determine whether this file was modified externally * @return The time the file was last modified diff --git a/app/src/processing/app/Util.java b/app/src/processing/app/Util.java new file mode 100644 index 000000000..4317fb887 --- /dev/null +++ b/app/src/processing/app/Util.java @@ -0,0 +1,617 @@ +/* -*- 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 + Copyright (c) 2004-12 Ben Fry and Casey Reas + + 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +package processing.app; + +import java.io.*; +import java.util.Enumeration; +import java.util.Vector; +import java.util.zip.*; + +import processing.core.PApplet; +import processing.data.StringDict; +import processing.data.StringList; + + +public class Util { + + /** + * Get the number of lines in a file by counting the number of newline + * characters inside a String (and adding 1). + */ + static public int countLines(String what) { + int count = 1; + for (char c : what.toCharArray()) { + if (c == '\n') count++; + } + return count; + } + + + /** + * Same as PApplet.loadBytes(), however never does gzip decoding. + */ + static public byte[] loadBytesRaw(File file) throws IOException { + int size = (int) file.length(); + FileInputStream input = new FileInputStream(file); + byte buffer[] = new byte[size]; + int offset = 0; + int bytesRead; + while ((bytesRead = input.read(buffer, offset, size-offset)) != -1) { + offset += bytesRead; + if (bytesRead == 0) break; + } + input.close(); // weren't properly being closed + input = null; + return buffer; + } + + + /** + * Read from a file with a bunch of attribute/value pairs + * that are separated by = and ignore comments with #. + * Changed in 3.x to return null (rather than empty hash) if no file, + * and changed return type to StringDict instead of Map or HashMap. + */ + static public StringDict readSettings(File inputFile) { + if (!inputFile.exists()) { + if (Base.DEBUG) System.err.println(inputFile + " does not exist."); + return null; + } + String lines[] = PApplet.loadStrings(inputFile); + if (lines == null) { + System.err.println("Could not read " + inputFile); + return null; + } + return readSettings(inputFile.toString(), lines); + } + + + /** + * Parse a String array that contains attribute/value pairs separated + * by = (the equals sign). The # (hash) symbol is used to denote comments. + * Comments can be anywhere on a line. Blank lines are ignored. + * In 3.0a6, no longer taking a blank HashMap as param; no cases in the main + * PDE code of adding to a (Hash)Map. Also returning the Map instead of void. + * Both changes modify the method signature, but this was only used by the + * contrib classes. + */ + static public StringDict readSettings(String filename, String[] lines) { + StringDict settings = new StringDict(); + for (String line : lines) { + // Remove comments + int commentMarker = line.indexOf('#'); + if (commentMarker != -1) { + line = line.substring(0, commentMarker); + } + // Remove extra whitespace + line = line.trim(); + + if (line.length() != 0) { + int equals = line.indexOf('='); + if (equals == -1) { + if (filename != null) { + System.err.println("Ignoring illegal line in " + filename); + System.err.println(" " + line); + } + } else { + String attr = line.substring(0, equals).trim(); + String valu = line.substring(equals + 1).trim(); + settings.set(attr, valu); + } + } + } + return settings; + } + + + static public void copyFile(File sourceFile, + File targetFile) throws IOException { + BufferedInputStream from = + new BufferedInputStream(new FileInputStream(sourceFile)); + BufferedOutputStream to = + new BufferedOutputStream(new FileOutputStream(targetFile)); + byte[] buffer = new byte[16 * 1024]; + int bytesRead; + while ((bytesRead = from.read(buffer)) != -1) { + to.write(buffer, 0, bytesRead); + } + from.close(); + from = null; + + to.flush(); + to.close(); + to = null; + + targetFile.setLastModified(sourceFile.lastModified()); + targetFile.setExecutable(sourceFile.canExecute()); + } + + + /** + * Grab the contents of a file as a string. + */ + static public String loadFile(File file) throws IOException { + String[] contents = PApplet.loadStrings(file); + if (contents == null) return null; + return PApplet.join(contents, "\n"); + } + + + /** + * Spew the contents of a String object out to a file. + */ + static public void saveFile(String str, File file) throws IOException { + File temp = File.createTempFile(file.getName(), null, file.getParentFile()); + try { + // fix from cjwant to prevent symlinks from being destroyed. + File canon = file.getCanonicalFile(); + // assign the var as second step since previous line may throw exception + file = canon; + } catch (IOException e) { + throw new IOException("Could not resolve canonical representation of " + + file.getAbsolutePath()); + } + // Can't use saveStrings() here b/c Windows will add a ^M to the file + PrintWriter writer = PApplet.createWriter(temp); + writer.print(str); + boolean error = writer.checkError(); // calls flush() + writer.close(); // attempt to close regardless + if (error) { + throw new IOException("Error while trying to save " + file); + } + + // remove the old file before renaming the temp file + if (file.exists()) { + boolean result = file.delete(); + if (!result) { + throw new IOException("Could not remove old version of " + + file.getAbsolutePath()); + } + } + boolean result = temp.renameTo(file); + if (!result) { + throw new IOException("Could not replace " + file.getAbsolutePath() + + " with " + temp.getAbsolutePath()); + } + } + + + /** + * Copy a folder from one place to another. This ignores all dot files and + * folders found in the source directory, to avoid copying silly .DS_Store + * files and potentially troublesome .svn folders. + */ + static public void copyDir(File sourceDir, + File targetDir) throws IOException { + if (sourceDir.equals(targetDir)) { + final String urDum = "source and target directories are identical"; + throw new IllegalArgumentException(urDum); + } + targetDir.mkdirs(); + String files[] = sourceDir.list(); + for (int i = 0; i < files.length; i++) { + // Ignore dot files (.DS_Store), dot folders (.svn) while copying + if (files[i].charAt(0) == '.') continue; + //if (files[i].equals(".") || files[i].equals("..")) continue; + File source = new File(sourceDir, files[i]); + File target = new File(targetDir, files[i]); + if (source.isDirectory()) { + //target.mkdirs(); + copyDir(source, target); + target.setLastModified(source.lastModified()); + } else { + copyFile(source, target); + } + } + } + + + static public void copyDirNative(File sourceDir, + File targetDir) throws IOException { + Process process = null; + if (Base.isMacOS() || Base.isLinux()) { + process = Runtime.getRuntime().exec(new String[] { + "cp", "-a", sourceDir.getAbsolutePath(), targetDir.getAbsolutePath() + }); + } else { + // TODO implement version that uses XCOPY here on Windows + throw new RuntimeException("Not yet implemented on Windows"); + } + try { + int result = process.waitFor(); + if (result != 0) { + throw new IOException("Error while copying (result " + result + ")"); + } + } catch (InterruptedException e) { + e.printStackTrace(); + } + } + + +// /** +// * Delete a file or directory in a platform-specific manner. Removes a File +// * object (a file or directory) from the system by placing it in the Trash +// * or Recycle Bin (if available) or simply deleting it (if not). +// * +// * When the file/folder is on another file system, it may simply be removed +// * immediately, without additional warning. So only use this if you want to, +// * you know, "delete" the subject in question. +// * +// * NOTE: Not yet tested nor ready for prime-time. +// * +// * @param file the victim (a directory or individual file) +// * @return true if all ends well +// * @throws IOException what went wrong +// */ +// static public boolean platformDelete(File file) throws IOException { +// return Base.getPlatform().deleteFile(file); +// } + + + /** + * Remove all files in a directory and the directory itself. + */ + static public void removeDir(File dir) { + if (dir.exists()) { + removeDescendants(dir); + if (!dir.delete()) { + System.err.println("Could not delete " + dir); + } + } + } + + + /** + * Recursively remove all files within a directory, + * used with removeDir(), or when the contents of a dir + * should be removed, but not the directory itself. + * (i.e. when cleaning temp files from lib/build) + */ + static public void removeDescendants(File dir) { + if (!dir.exists()) return; + + String files[] = dir.list(); + for (int i = 0; i < files.length; i++) { + if (files[i].equals(".") || files[i].equals("..")) continue; + File dead = new File(dir, files[i]); + if (!dead.isDirectory()) { + if (!Preferences.getBoolean("compiler.save_build_files")) { + if (!dead.delete()) { + // temporarily disabled + System.err.println("Could not delete " + dead); + } + } + } else { + removeDir(dead); + //dead.delete(); + } + } + } + + + /** + * Calculate the size of the contents of a folder. + * Used to determine whether sketches are empty or not. + * Note that the function calls itself recursively. + */ + static public int calcFolderSize(File folder) { + int size = 0; + + String files[] = folder.list(); + // null if folder doesn't exist, happens when deleting sketch + if (files == null) return -1; + + for (int i = 0; i < files.length; i++) { + if (files[i].equals(".") || + files[i].equals("..") || + files[i].equals(".DS_Store")) continue; + File fella = new File(folder, files[i]); + if (fella.isDirectory()) { + size += calcFolderSize(fella); + } else { + size += (int) fella.length(); + } + } + return size; + } + + +// /** +// * Recursively creates a list of all files within the specified folder, +// * and returns a list of their relative paths. +// * Ignores any files/folders prefixed with a dot. +// */ +// static public String[] listFiles(String path, boolean relative) { +// return listFiles(new File(path), relative); +// } + + + static public String[] listFiles(File folder, boolean relative) { + String path = folder.getAbsolutePath(); + Vector vector = new Vector(); + listFiles(relative ? (path + File.separator) : "", path, null, vector); + String outgoing[] = new String[vector.size()]; + vector.copyInto(outgoing); + return outgoing; + } + + + static public String[] listFiles(File folder, boolean relative, String extension) { + String path = folder.getAbsolutePath(); + Vector vector = new Vector(); + if (extension != null) { + if (!extension.startsWith(".")) { + extension = "." + extension; + } + } + listFiles(relative ? (path + File.separator) : "", path, extension, vector); + String outgoing[] = new String[vector.size()]; + vector.copyInto(outgoing); + return outgoing; + } + + + static protected void listFiles(String basePath, + String path, String extension, + Vector vector) { + File folder = new File(path); + String[] list = folder.list(); + if (list != null) { + for (String item : list) { + if (item.charAt(0) == '.') continue; + if (extension == null || item.toLowerCase().endsWith(extension)) { + File file = new File(path, item); + String newPath = file.getAbsolutePath(); + if (newPath.startsWith(basePath)) { + newPath = newPath.substring(basePath.length()); + } + // only add if no ext or match + if (extension == null || item.toLowerCase().endsWith(extension)) { + vector.add(newPath); + } + if (file.isDirectory()) { // use absolute path + listFiles(basePath, file.getAbsolutePath(), extension, vector); + } + } + } + } + } + + + /** + * @param folder source folder to search + * @return a list of .jar and .zip files in that folder + */ + static public File[] listJarFiles(File folder) { + return folder.listFiles(new FilenameFilter() { + public boolean accept(File dir, String name) { + return (!name.startsWith(".") && + (name.toLowerCase().endsWith(".jar") || + name.toLowerCase().endsWith(".zip"))); + } + }); + } + + + ///////////////////////////////////////////////////////////////////////////// + + + /** + * Given a folder, return a list of absolute paths to all jar or zip files + * inside that folder, separated by pathSeparatorChar. + * + * This will prepend a colon (or whatever the path separator is) + * so that it can be directly appended to another path string. + * + * As of 0136, this will no longer add the root folder as well. + * + * This function doesn't bother checking to see if there are any .class + * files in the folder or within a subfolder. + */ + static public String contentsToClassPath(File folder) { + if (folder == null) return ""; + + StringBuilder sb = new StringBuilder(); + String sep = System.getProperty("path.separator"); + + try { + String path = folder.getCanonicalPath(); + + // When getting the name of this folder, make sure it has a slash + // after it, so that the names of sub-items can be added. + if (!path.endsWith(File.separator)) { + path += File.separator; + } + + String list[] = folder.list(); + for (int i = 0; i < list.length; i++) { + // Skip . and ._ files. Prior to 0125p3, .jar files that had + // OS X AppleDouble files associated would cause trouble. + if (list[i].startsWith(".")) continue; + + if (list[i].toLowerCase().endsWith(".jar") || + list[i].toLowerCase().endsWith(".zip")) { + sb.append(sep); + sb.append(path); + sb.append(list[i]); + } + } + } catch (IOException e) { + e.printStackTrace(); // this would be odd + } + return sb.toString(); + } + + + /** + * A classpath, separated by the path separator, will contain + * a series of .jar/.zip files or directories containing .class + * files, or containing subdirectories that have .class files. + * + * @param path the input classpath + * @return array of possible package names + */ + static public StringList packageListFromClassPath(String path) { +// Map map = new HashMap(); + StringList list = new StringList(); + String pieces[] = + PApplet.split(path, File.pathSeparatorChar); + + for (int i = 0; i < pieces.length; i++) { + //System.out.println("checking piece '" + pieces[i] + "'"); + if (pieces[i].length() == 0) continue; + + if (pieces[i].toLowerCase().endsWith(".jar") || + pieces[i].toLowerCase().endsWith(".zip")) { + //System.out.println("checking " + pieces[i]); + packageListFromZip(pieces[i], list); + + } else { // it's another type of file or directory + File dir = new File(pieces[i]); + if (dir.exists() && dir.isDirectory()) { + packageListFromFolder(dir, null, list); + //importCount = magicImportsRecursive(dir, null, + // map); + //imports, importCount); + } + } + } +// int mapCount = map.size(); +// String output[] = new String[mapCount]; +// int index = 0; +// Set set = map.keySet(); +// for (String s : set) { +// output[index++] = s.replace('/', '.'); +// } +// return output; + StringList outgoing = new StringList(list.size()); + for (String item : list) { + outgoing.append(item.replace('/', '.')); + } + return outgoing; + } + + + static private void packageListFromZip(String filename, StringList list) { + try { + ZipFile file = new ZipFile(filename); + Enumeration entries = file.entries(); + while (entries.hasMoreElements()) { + ZipEntry entry = (ZipEntry) entries.nextElement(); + + if (!entry.isDirectory()) { + String name = entry.getName(); + + if (name.endsWith(".class")) { + int slash = name.lastIndexOf('/'); + if (slash == -1) continue; + + String pname = name.substring(0, slash); +// if (map.get(pname) == null) { +// map.put(pname, new Object()); +// } + list.appendUnique(pname); + } + } + } + file.close(); + } catch (IOException e) { + System.err.println("Ignoring " + filename + " (" + e.getMessage() + ")"); + //e.printStackTrace(); + } + } + + + /** + * Make list of package names by traversing a directory hierarchy. + * Each time a class is found in a folder, add its containing set + * of folders to the package list. If another folder is found, + * walk down into that folder and continue. + */ + static private void packageListFromFolder(File dir, String sofar, + StringList list) { +// Map map) { + boolean foundClass = false; + String files[] = dir.list(); + + for (int i = 0; i < files.length; i++) { + if (files[i].equals(".") || files[i].equals("..")) continue; + + File sub = new File(dir, files[i]); + if (sub.isDirectory()) { + String nowfar = + (sofar == null) ? files[i] : (sofar + "." + files[i]); + packageListFromFolder(sub, nowfar, list); + //System.out.println(nowfar); + //imports[importCount++] = nowfar; + //importCount = magicImportsRecursive(sub, nowfar, + // imports, importCount); + } else if (!foundClass) { // if no classes found in this folder yet + if (files[i].endsWith(".class")) { + //System.out.println("unique class: " + files[i] + " for " + sofar); +// map.put(sofar, new Object()); + list.appendUnique(sofar); + foundClass = true; + } + } + } + } + + + static public void unzip(File zipFile, File dest) { + try { + FileInputStream fis = new FileInputStream(zipFile); + CheckedInputStream checksum = new CheckedInputStream(fis, new Adler32()); + ZipInputStream zis = new ZipInputStream(new BufferedInputStream(checksum)); + ZipEntry next = null; + while ((next = zis.getNextEntry()) != null) { + File currentFile = new File(dest, next.getName()); + if (next.isDirectory()) { + currentFile.mkdirs(); + } else { + File parentDir = currentFile.getParentFile(); + // Sometimes the directory entries aren't already created + if (!parentDir.exists()) { + parentDir.mkdirs(); + } + currentFile.createNewFile(); + unzipEntry(zis, currentFile); + } + } + } catch (Exception e) { + e.printStackTrace(); + } + } + + + static protected void unzipEntry(ZipInputStream zin, File f) throws IOException { + FileOutputStream out = new FileOutputStream(f); + byte[] b = new byte[512]; + int len = 0; + while ((len = zin.read(b)) != -1) { + out.write(b, 0, len); + } + out.flush(); + out.close(); + } +} diff --git a/app/src/processing/app/contrib/AvailableContribution.java b/app/src/processing/app/contrib/AvailableContribution.java index 6cb710e0c..b3d8c1759 100644 --- a/app/src/processing/app/contrib/AvailableContribution.java +++ b/app/src/processing/app/contrib/AvailableContribution.java @@ -26,6 +26,7 @@ import java.io.*; import processing.app.Base; import processing.app.Language; +import processing.app.Util; import processing.core.PApplet; import processing.data.StringDict; import processing.data.StringList; @@ -107,7 +108,7 @@ public class AvailableContribution extends Contribution { status.setErrorMessage(Language.text("contrib.errors.temporary_directory")); return null; } - Base.unzip(contribArchive, tempFolder); + Util.unzip(contribArchive, tempFolder); // System.out.println("temp folder is " + tempFolder); // Base.openFolder(tempFolder); @@ -194,7 +195,7 @@ public class AvailableContribution extends Contribution { } // 4. Okay, now actually delete that temp folder - Base.removeDir(newContribFolder); + Util.removeDir(newContribFolder); } else { if (status != null) { @@ -205,7 +206,7 @@ public class AvailableContribution extends Contribution { // Remove any remaining boogers if (tempFolder.exists()) { - Base.removeDir(tempFolder); + Util.removeDir(tempFolder); } return installedContrib; } @@ -235,7 +236,7 @@ public class AvailableContribution extends Contribution { */ public boolean writePropertiesFile(File propFile) { try { - StringDict properties = Base.readSettings(propFile); + StringDict properties = Util.readSettings(propFile); String name = properties.get("name"); if (name == null || name.isEmpty()) diff --git a/app/src/processing/app/contrib/ContributionListing.java b/app/src/processing/app/contrib/ContributionListing.java index 75ffdcc86..2ff6f8850 100644 --- a/app/src/processing/app/contrib/ContributionListing.java +++ b/app/src/processing/app/contrib/ContributionListing.java @@ -28,6 +28,7 @@ import java.util.concurrent.locks.ReentrantLock; import processing.app.Base; import processing.app.Library; +import processing.app.Util; import processing.core.PApplet; import processing.data.StringDict; @@ -570,9 +571,7 @@ public class ContributionListing { } String[] contribLines = PApplet.subset(lines, start, end-start); - - StringDict contribParams = Base.readSettings(file.getName(), contribLines); - + StringDict contribParams = Util.readSettings(file.getName(), contribLines); outgoing.add(new AvailableContribution(contribType, contribParams)); start = end + 1; } diff --git a/app/src/processing/app/contrib/ContributionManager.java b/app/src/processing/app/contrib/ContributionManager.java index bcd5501c1..77ce02a54 100644 --- a/app/src/processing/app/contrib/ContributionManager.java +++ b/app/src/processing/app/contrib/ContributionManager.java @@ -30,6 +30,7 @@ import javax.swing.SwingWorker; import processing.app.Base; import processing.app.Language; +import processing.app.Util; import processing.app.ui.Editor; import processing.core.PApplet; import processing.data.StringDict; @@ -314,7 +315,7 @@ public class ContributionManager { return file.equals(ac.getType() + ".properties"); } }); - if (contents.length > 0 && Base.readSettings(contents[0]).get("name").equals(ac.getName())) { + if (contents.length > 0 && Util.readSettings(contents[0]).get("name").equals(ac.getName())) { return; } } @@ -553,7 +554,7 @@ public class ContributionManager { Iterator folderIter = deleteList.iterator(); while (folderIter.hasNext()) { - Base.removeDir(folderIter.next()); + Util.removeDir(folderIter.next()); } } @@ -572,7 +573,7 @@ public class ContributionManager { } }); for (File folder : markedForDeletion) { - Base.removeDir(folder); + Util.removeDir(folder); } } @@ -636,9 +637,9 @@ public class ContributionManager { propFileName = "libraries.properties"; for (File folder : markedForUpdate) { - StringDict props = Base.readSettings(new File(folder, propFileName)); + StringDict props = Util.readSettings(new File(folder, propFileName)); updateContribsNames.add(props.get("name")); - Base.removeDir(folder); + Util.removeDir(folder); } Iterator iter = contribListing.advertisedContributions.iterator(); diff --git a/app/src/processing/app/contrib/ExamplesContribution.java b/app/src/processing/app/contrib/ExamplesContribution.java index ebc3ff354..7dfb2dbb2 100644 --- a/app/src/processing/app/contrib/ExamplesContribution.java +++ b/app/src/processing/app/contrib/ExamplesContribution.java @@ -6,6 +6,7 @@ import java.util.List; import java.util.Map; import processing.app.Base; +import processing.app.Util; import processing.core.PApplet; import processing.data.StringDict; import processing.data.StringList; @@ -55,7 +56,7 @@ public class ExamplesContribution extends LocalContribution { new File(exampleFolder, EXAMPLES.getPropertiesName()); if (propertiesFile.exists()) { StringList compatibleList = - parseModeList(Base.readSettings(propertiesFile)); + parseModeList(Util.readSettings(propertiesFile)); if (compatibleList.size() == 0) { return true; // if no mode specified, just include everywhere } diff --git a/app/src/processing/app/contrib/LocalContribution.java b/app/src/processing/app/contrib/LocalContribution.java index 6508bf87e..3ac2d21aa 100644 --- a/app/src/processing/app/contrib/LocalContribution.java +++ b/app/src/processing/app/contrib/LocalContribution.java @@ -59,7 +59,7 @@ public abstract class LocalContribution extends Contribution { // required for contributed modes, but not for built-in core modes File propertiesFile = new File(folder, getTypeName() + ".properties"); if (propertiesFile.exists()) { - properties = Base.readSettings(propertiesFile); + properties = Util.readSettings(propertiesFile); name = properties.get("name"); id = properties.get("id"); @@ -153,7 +153,7 @@ public abstract class LocalContribution extends Contribution { } // Add .jar and .zip files from the "mode" folder into the classpath - File[] archives = Base.listJarFiles(modeDirectory); + File[] archives = Util.listJarFiles(modeDirectory); if (archives != null && archives.length > 0) { URL[] urlList = new URL[archives.length]; for (int j = 0; j < urlList.length; j++) { @@ -301,14 +301,15 @@ public abstract class LocalContribution extends Contribution { // At this point it should be safe to replace this fella if (contribFolder.exists()) { - Base.removeDir(contribFolder); + Util.removeDir(contribFolder); } } else { - // This if should ideally never happen, since this function is to be called only when restarting on update + // This if should ideally never happen, since this function + // is to be called only when restarting on update if (contribFolder.exists() && contribFolder.isDirectory()) { - Base.removeDir(contribFolder); + Util.removeDir(contribFolder); } else if (contribFolder.exists()) { contribFolder.delete(); @@ -319,7 +320,7 @@ public abstract class LocalContribution extends Contribution { File oldFolder = getFolder(); try { - Base.copyDir(oldFolder, contribFolder); + Util.copyDir(oldFolder, contribFolder); } catch (IOException e) { status.setErrorMessage("Could not copy " + getTypeName() + " \"" + getName() + "\" to the sketchbook."); @@ -361,7 +362,7 @@ public abstract class LocalContribution extends Contribution { success = getFolder().renameTo(backupSubFolder); } else { try { - Base.copyDir(getFolder(), backupSubFolder); + Util.copyDir(getFolder(), backupSubFolder); success = true; } catch (IOException e) { } } @@ -440,7 +441,7 @@ public abstract class LocalContribution extends Contribution { if (doBackup) { success = backup(editor, true, status); } else { - Base.removeDir(getFolder()); + Util.removeDir(getFolder()); success = !getFolder().exists(); } diff --git a/app/src/processing/app/contrib/ModeContribution.java b/app/src/processing/app/contrib/ModeContribution.java index bcc0b4b0d..a81ee68e3 100644 --- a/app/src/processing/app/contrib/ModeContribution.java +++ b/app/src/processing/app/contrib/ModeContribution.java @@ -33,6 +33,7 @@ import java.util.Map; import processing.app.Base; import processing.app.Mode; +import processing.app.Util; public class ModeContribution extends LocalContribution { @@ -215,8 +216,8 @@ public class ModeContribution extends LocalContribution { for(String modeImport: imports){ if (installedModes.containsKey(modeImport)) { Base.log("Found mode dependency " + modeImport); - File[] archives = Base.listJarFiles(new File(installedModes.get(modeImport). - getFolder().getAbsolutePath() + File.separator + "mode")); + File modeFolder = installedModes.get(modeImport).getFolder(); + File[] archives = Util.listJarFiles(new File(modeFolder, "mode")); if (archives != null && archives.length > 0) { for (int i = 0; i < archives.length; i++) { // Base.log("Adding jar dependency: " + archives[i].getAbsolutePath()); @@ -231,7 +232,7 @@ public class ModeContribution extends LocalContribution { } // Add .jar and .zip files from the "mode" folder into the classpath - File[] archives = Base.listJarFiles(modeDirectory); + File[] archives = Util.listJarFiles(modeDirectory); if (archives != null && archives.length > 0) { int arrLen = archives.length + extraUrls.size(); URL[] urlList = new URL[arrLen]; diff --git a/app/src/processing/app/tools/Archiver.java b/app/src/processing/app/tools/Archiver.java index 56b7d49a9..16fa5fc69 100644 --- a/app/src/processing/app/tools/Archiver.java +++ b/app/src/processing/app/tools/Archiver.java @@ -159,7 +159,7 @@ public class Archiver implements Tool { ZipEntry entry = new ZipEntry(nowfar); entry.setTime(sub.lastModified()); zos.putNextEntry(entry); - zos.write(Base.loadBytesRaw(sub)); + zos.write(Util.loadBytesRaw(sub)); zos.closeEntry(); } } diff --git a/app/src/processing/app/ui/Editor.java b/app/src/processing/app/ui/Editor.java index b318e51d7..7e1d25747 100644 --- a/app/src/processing/app/ui/Editor.java +++ b/app/src/processing/app/ui/Editor.java @@ -32,6 +32,7 @@ import processing.app.RunnerListener; import processing.app.Sketch; import processing.app.SketchCode; import processing.app.SketchException; +import processing.app.Util; import processing.app.contrib.ToolContribution; import processing.app.syntax.*; import processing.app.tools.*; @@ -2691,7 +2692,7 @@ public abstract class Editor extends JFrame implements RunnerListener { File properPdeFile = new File(properFolder, file.getName()); File origPdeFile = new File(path); try { - Base.copyFile(origPdeFile, properPdeFile); + Util.copyFile(origPdeFile, properPdeFile); } catch (IOException e) { Base.showWarning("Error", "Could not copy to a proper location.", e); return false; diff --git a/app/src/processing/app/ui/ProgressFrame.java b/app/src/processing/app/ui/ProgressFrame.java index f655d36da..0dcab2e87 100644 --- a/app/src/processing/app/ui/ProgressFrame.java +++ b/app/src/processing/app/ui/ProgressFrame.java @@ -15,8 +15,8 @@ import javax.swing.JPanel; import javax.swing.JProgressBar; import javax.swing.SwingWorker; -import processing.app.Base; import processing.app.Language; +import processing.app.Util; // TODO This code was contributed and needs a lot of work. [fry] @@ -324,7 +324,7 @@ public class ProgressFrame extends JFrame implements PropertyChangeListener { * the component files and sub-folders if passed. */ long calcSize(File file) { - return file.isFile() ? file.length() : Base.calcFolderSize(file); + return file.isFile() ? file.length() : Util.calcFolderSize(file); } diff --git a/java/src/processing/mode/java/Commander.java b/java/src/processing/mode/java/Commander.java index 1d11529dd..4d3ecf0e4 100644 --- a/java/src/processing/mode/java/Commander.java +++ b/java/src/processing/mode/java/Commander.java @@ -32,6 +32,7 @@ import processing.app.Preferences; import processing.app.RunnerListener; import processing.app.Sketch; import processing.app.SketchException; +import processing.app.Util; import processing.app.contrib.ModeContribution; import processing.core.PApplet; import processing.mode.java.runner.Runner; @@ -208,7 +209,7 @@ public class Commander implements RunnerListener { outputFolder = new File(outputPath); if (outputFolder.exists()) { if (force) { - Base.removeDir(outputFolder); + Util.removeDir(outputFolder); } else { complainAndQuit("The output folder already exists. " + "Use --force to remove it.", false); diff --git a/java/src/processing/mode/java/Compiler.java b/java/src/processing/mode/java/Compiler.java index ca1786d73..c62b4c000 100644 --- a/java/src/processing/mode/java/Compiler.java +++ b/java/src/processing/mode/java/Compiler.java @@ -46,8 +46,8 @@ public class Compiler { importSuggestions.put("Frame", "java.awt.Frame"); importSuggestions.put("Iterator", "java.util.Iterator"); } - - + + /** * Compile with ECJ. See http://j.mp/8paifz for documentation. * @@ -74,7 +74,7 @@ public class Compiler { }; //PApplet.println(baseCommand); - String[] sourceFiles = Base.listFiles(build.getSrcFolder(), false, ".java"); + String[] sourceFiles = Util.listFiles(build.getSrcFolder(), false, ".java"); String[] command = PApplet.concat(baseCommand, sourceFiles); //PApplet.println(command); @@ -96,13 +96,13 @@ public class Compiler { PrintWriter writer = new PrintWriter(internalWriter); //result = com.sun.tools.javac.Main.compile(command, writer); - + PrintWriter outWriter = new PrintWriter(System.out); - + // Version that's not dynamically loaded //CompilationProgress progress = null; //success = BatchCompiler.compile(command, outWriter, writer, progress); - + // Version that *is* dynamically loaded. First gets the mode class loader // so that it can grab the compiler JAR files from it. ClassLoader loader = build.mode.getClassLoader(); @@ -114,13 +114,13 @@ public class Compiler { Class[] compileArgs = new Class[] { String[].class, PrintWriter.class, PrintWriter.class, progressClass }; Method compileMethod = batchClass.getMethod("compile", compileArgs); - success = (Boolean) + success = (Boolean) compileMethod.invoke(null, new Object[] { command, outWriter, writer, null }); } catch (Exception e) { e.printStackTrace(); throw new SketchException("Unknown error inside the compiler."); } - + // Close out the stream for good measure writer.flush(); writer.close(); @@ -207,7 +207,7 @@ public class Compiler { } else { exception.setMessage("Cannot find a class or type " + "named \u201C" + what + "\u201D"); - + String suggestion = importSuggestions.get(what); if (suggestion != null) { System.err.println("You may need to add \"import " + suggestion + ";\" to the top of your sketch."); diff --git a/java/src/processing/mode/java/ErrorColumn.java b/java/src/processing/mode/java/ErrorColumn.java index 779ac7d6c..490a9e7a4 100644 --- a/java/src/processing/mode/java/ErrorColumn.java +++ b/java/src/processing/mode/java/ErrorColumn.java @@ -36,8 +36,8 @@ import javax.swing.JPanel; import javax.swing.SwingWorker; import javax.swing.text.BadLocationException; -import processing.app.Base; import processing.app.SketchCode; +import processing.app.Util; import processing.mode.java.pdex.ErrorCheckerService; import processing.mode.java.pdex.ErrorMarker; import processing.mode.java.pdex.Problem; @@ -165,7 +165,7 @@ public class ErrorColumn extends JPanel { int totalLines = 0, currentTab = editor.getSketch() .getCurrentCodeIndex(); try { - totalLines = Base.countLines(sc.getDocument() + totalLines = Util.countLines(sc.getDocument() .getText(0, sc.getDocument().getLength())) + 1; } catch (BadLocationException e) { e.printStackTrace(); diff --git a/java/src/processing/mode/java/JavaBuild.java b/java/src/processing/mode/java/JavaBuild.java index d06a0ef5b..3856dd5cc 100644 --- a/java/src/processing/mode/java/JavaBuild.java +++ b/java/src/processing/mode/java/JavaBuild.java @@ -221,12 +221,12 @@ public class JavaBuild { // get a list of .jar files in the "code" folder // (class files in subfolders should also be picked up) String codeFolderClassPath = - Base.contentsToClassPath(codeFolder); + Util.contentsToClassPath(codeFolder); // append the jar files in the code folder to the class path classPath += File.pathSeparator + codeFolderClassPath; // get list of packages found in those jars codeFolderPackages = - Base.packageListFromClassPath(codeFolderClassPath); + Util.packageListFromClassPath(codeFolderClassPath); } else { javaLibraryPath = ""; @@ -520,7 +520,7 @@ public class JavaBuild { } File packageFolder = new File(srcFolder, packageMatch[0].replace('.', '/')); packageFolder.mkdirs(); - Base.saveFile(javaCode, new File(packageFolder, filename)); + Util.saveFile(javaCode, new File(packageFolder, filename)); } } catch (IOException e) { @@ -897,7 +897,7 @@ public class JavaBuild { File macosFolder = new File(contentsFolder, "MacOS"); macosFolder.mkdirs(); - Base.copyFile(new File(contentsOrig, "MacOS/Processing"), + Util.copyFile(new File(contentsOrig, "MacOS/Processing"), new File(contentsFolder, "MacOS/" + sketch.getName())); File pkgInfo = new File(contentsFolder, "PkgInfo"); @@ -908,14 +908,14 @@ public class JavaBuild { // Use faster(?) native copy here (also to do sym links) if (embedJava) { - Base.copyDirNative(new File(contentsOrig, "PlugIns"), + Util.copyDirNative(new File(contentsOrig, "PlugIns"), new File(contentsFolder, "PlugIns")); } File resourcesFolder = new File(contentsFolder, "Resources"); - Base.copyDir(new File(contentsOrig, "Resources/en.lproj"), + Util.copyDir(new File(contentsOrig, "Resources/en.lproj"), new File(resourcesFolder, "en.lproj")); - Base.copyFile(mode.getContentFile("application/sketch.icns"), + Util.copyFile(mode.getContentFile("application/sketch.icns"), new File(resourcesFolder, "sketch.icns")); /* @@ -944,12 +944,12 @@ public class JavaBuild { */ } else if (exportPlatform == PConstants.LINUX) { if (embedJava) { - Base.copyDirNative(Base.getJavaHome(), new File(destFolder, "java")); + Util.copyDirNative(Base.getJavaHome(), new File(destFolder, "java")); } } else if (exportPlatform == PConstants.WINDOWS) { if (embedJava) { - Base.copyDir(Base.getJavaHome(), new File(destFolder, "java")); + Util.copyDir(Base.getJavaHome(), new File(destFolder, "java")); } } @@ -1022,15 +1022,15 @@ public class JavaBuild { // 'data' folder next to 'lib'. if (sketch.hasDataFolder()) { if (exportPlatform == PConstants.MACOSX) { - Base.copyDir(sketch.getDataFolder(), new File(jarFolder, "data")); + Util.copyDir(sketch.getDataFolder(), new File(jarFolder, "data")); } else { - Base.copyDir(sketch.getDataFolder(), new File(destFolder, "data")); + Util.copyDir(sketch.getDataFolder(), new File(destFolder, "data")); } } // add the contents of the code folder to the jar if (sketch.hasCodeFolder()) { - String includes = Base.contentsToClassPath(sketch.getCodeFolder()); + String includes = Util.contentsToClassPath(sketch.getCodeFolder()); // Use tokens to get rid of extra blanks, which causes huge exports String[] codeList = PApplet.splitTokens(includes, File.pathSeparator); for (int i = 0; i < codeList.length; i++) { @@ -1038,7 +1038,7 @@ public class JavaBuild { codeList[i].toLowerCase().endsWith(".zip")) { File exportFile = new File(codeList[i]); String exportFilename = exportFile.getName(); - Base.copyFile(exportFile, new File(jarFolder, exportFilename)); + Util.copyFile(exportFile, new File(jarFolder, exportFilename)); jarListVector.add(exportFilename); } else { // cp += codeList[i] + File.pathSeparator; @@ -1064,17 +1064,17 @@ public class JavaBuild { "a big fat lie and does not exist."); } else if (exportFile.isDirectory()) { - Base.copyDir(exportFile, new File(jarFolder, exportName)); + Util.copyDir(exportFile, new File(jarFolder, exportName)); } else if (exportName.toLowerCase().endsWith(".zip") || exportName.toLowerCase().endsWith(".jar")) { - Base.copyFile(exportFile, new File(jarFolder, exportName)); + Util.copyFile(exportFile, new File(jarFolder, exportName)); jarListVector.add(exportName); } else { // Starting with 2.0a2 put extra export files (DLLs, plugins folder, // anything else for libraries) inside lib or Contents/Resources/Java - Base.copyFile(exportFile, new File(jarFolder, exportName)); + Util.copyFile(exportFile, new File(jarFolder, exportName)); } } } @@ -1306,7 +1306,7 @@ public class JavaBuild { String preprocFilename = sketch.getName() + ".java"; File preprocFile = new File(srcFolder, preprocFilename); if (preprocFile.exists()) { - Base.copyFile(preprocFile, new File(sourceFolder, preprocFilename)); + Util.copyFile(preprocFile, new File(sourceFolder, preprocFilename)); } else { System.err.println("Could not copy source file: " + preprocFile.getAbsolutePath()); } @@ -1422,7 +1422,7 @@ public class JavaBuild { protected void addDataFolder(ZipOutputStream zos) throws IOException { if (sketch.hasDataFolder()) { - String[] dataFiles = Base.listFiles(sketch.getDataFolder(), false); + String[] dataFiles = Util.listFiles(sketch.getDataFolder(), false); int offset = sketch.getFolder().getAbsolutePath().length() + 1; for (String path : dataFiles) { if (Base.isWindows()) { diff --git a/java/src/processing/mode/java/JavaEditor.java b/java/src/processing/mode/java/JavaEditor.java index e8d3f3f6c..482e56191 100644 --- a/java/src/processing/mode/java/JavaEditor.java +++ b/java/src/processing/mode/java/JavaEditor.java @@ -1285,7 +1285,7 @@ public class JavaEditor extends Editor { StringList list = lib.getImports(); // ask the library for its imports if (list == null) { // Default to old behavior and load each package in the primary jar - list = Base.packageListFromClassPath(lib.getJarPath()); + list = Util.packageListFromClassPath(lib.getJarPath()); } StringBuilder sb = new StringBuilder(); diff --git a/java/src/processing/mode/java/pdex/ASTGenerator.java b/java/src/processing/mode/java/pdex/ASTGenerator.java index da4db7afd..028d60048 100644 --- a/java/src/processing/mode/java/pdex/ASTGenerator.java +++ b/java/src/processing/mode/java/pdex/ASTGenerator.java @@ -109,6 +109,7 @@ import org.jsoup.select.Elements; import processing.app.Base; import processing.app.Library; import processing.app.SketchCode; +import processing.app.Util; import processing.app.syntax.JEditTextArea; import processing.app.ui.Toolkit; import processing.mode.java.JavaEditor; @@ -304,8 +305,9 @@ public class ASTGenerator { protected void loadJars() { factory = new ClassPathFactory(); - StringBuilder tehPath = new StringBuilder(System - .getProperty("java.class.path")); + StringBuilder tehPath = + new StringBuilder(System.getProperty("java.class.path")); + // Starting with JDK 1.7, no longer using Apple's Java, so // rt.jar has the same path on all OSes tehPath.append(File.pathSeparatorChar @@ -837,7 +839,7 @@ public class ASTGenerator { if (codeIndex > 0) for (int i = 0; i < codeIndex; i++) { SketchCode sc = editor.getSketch().getCode(i); - int len = Base.countLines(sc.getProgram()) + 1; + int len = Util.countLines(sc.getProgram()) + 1; lineNumber += len; } @@ -1636,7 +1638,7 @@ public class ASTGenerator { if (codeIndex > 0) { for (int i = 0; i < codeIndex; i++) { SketchCode sc = editor.getSketch().getCode(i); - int len = Base.countLines(sc.getProgram()) + 1; + int len = Util.countLines(sc.getProgram()) + 1; pdeLineNumber += len; } } @@ -2386,7 +2388,7 @@ public class ASTGenerator { if (codeIndex > 0) for (int i = 0; i < codeIndex; i++) { SketchCode sc = editor.getSketch().getCode(i); - int len = Base.countLines(sc.getProgram()) + 1; + int len = Util.countLines(sc.getProgram()) + 1; javaLineNumber += len; } return javaLineNumber; @@ -3327,8 +3329,7 @@ public class ASTGenerator { File codeFolder = editor.getSketch().getCodeFolder(); // get a list of .jar files in the "code" folder // (class files in subfolders should also be picked up) - ClassPath cp = factory.createFromPath(Base - .contentsToClassPath(codeFolder)); + ClassPath cp = factory.createFromPath(Util.contentsToClassPath(codeFolder)); resources = cp.findResources("", regf); for (String res : resources) { candidates.add(res); @@ -3393,8 +3394,8 @@ public class ASTGenerator { File codeFolder = editor.getSketch().getCodeFolder(); // get a list of .jar files in the "code" folder // (class files in subfolders should also be picked up) - ClassPath cp = factory.createFromPath(Base - .contentsToClassPath(codeFolder)); + ClassPath cp = + factory.createFromPath(Util.contentsToClassPath(codeFolder)); resources = cp.findResources("", regf); for (String res : resources) { candidates.add(res); diff --git a/java/src/processing/mode/java/pdex/ErrorCheckerService.java b/java/src/processing/mode/java/pdex/ErrorCheckerService.java index 8c88f1aa1..c7ecc2473 100644 --- a/java/src/processing/mode/java/pdex/ErrorCheckerService.java +++ b/java/src/processing/mode/java/pdex/ErrorCheckerService.java @@ -52,6 +52,7 @@ import org.eclipse.jdt.internal.compiler.impl.CompilerOptions; import processing.app.Base; import processing.app.Library; import processing.app.SketchCode; +import processing.app.Util; import processing.app.syntax.SyntaxDocument; import processing.app.ui.Editor; import processing.app.ui.EditorStatus; @@ -875,7 +876,7 @@ public class ErrorCheckerService implements Runnable { // get a list of .jar files in the "code" folder // (class files in subfolders should also be picked up) - String codeFolderClassPath = Base.contentsToClassPath(codeFolder); + String codeFolderClassPath = Util.contentsToClassPath(codeFolder); codeFolderChecked = true; // huh? doesn't this mean .length() == 0? [fry] if (codeFolderClassPath.equalsIgnoreCase("")) { @@ -1098,10 +1099,9 @@ public class ErrorCheckerService implements Runnable { if (sc.isExtension("pde")) { int len = 0; if (editor.getSketch().getCurrentCode().equals(sc)) { - len = Base.countLines(sc.getDocument().getText(0, - sc.getDocument().getLength())) + 1; + len = Util.countLines(sc.getDocument().getText(0, sc.getDocument().getLength())) + 1; } else { - len = Base.countLines(sc.getProgram()) + 1; + len = Util.countLines(sc.getProgram()) + 1; } // log("x,len, CI: " + x + "," + len + "," @@ -1185,10 +1185,9 @@ public class ErrorCheckerService implements Runnable { if (sc.isExtension("pde")) { int len = 0; if (editor.getSketch().getCurrentCode().equals(sc)) { - len = Base.countLines(sc.getDocument().getText(0, - sc.getDocument().getLength())) + 1; + len = Util.countLines(sc.getDocument().getText(0, sc.getDocument().getLength())) + 1; } else { - len = Base.countLines(sc.getProgram()) + 1; + len = Util.countLines(sc.getProgram()) + 1; } // log("x,len, CI: " + x + "," + len + "," @@ -1240,7 +1239,7 @@ public class ErrorCheckerService implements Runnable { int jLineNum = programImports.size() + 1; for (int i = 0; i < tab; i++) { SketchCode sc = editor.getSketch().getCode(i); - int len = Base.countLines(sc.getProgram()) + 1; + int len = Util.countLines(sc.getProgram()) + 1; jLineNum += len; } return jLineNum; @@ -1466,7 +1465,7 @@ public class ErrorCheckerService implements Runnable { // exception all the time would cause the editor to shut down over // trivial/recoverable quirks. It's the least bad option. [fry] final Document doc = editor.getTextArea().getDocument(); - final int lineCount = Base.countLines(doc.getText(0, doc.getLength())); + final int lineCount = Util.countLines(doc.getText(0, doc.getLength())); if (p.getLineNumber() < lineCount && p.getLineNumber() >= 0) { editor.getTextArea().scrollTo(p.getLineNumber(), 0); } @@ -1581,8 +1580,8 @@ public class ErrorCheckerService implements Runnable { // log(" - " // + Base.countLines(tabSource.substring(0, idx)) + " tab " // + tabNumber); - programImports.add(new ImportStatement(piece, tabNumber, Base - .countLines(tabSource.substring(0, idx)))); + int lineCount = Util.countLines(tabSource.substring(0, idx)); + programImports.add(new ImportStatement(piece, tabNumber, lineCount)); // Remove the import from the main program // Substitute with white spaces String whiteSpace = ""; diff --git a/todo.txt b/todo.txt index 55180b221..98d6249e4 100644 --- a/todo.txt +++ b/todo.txt @@ -28,6 +28,7 @@ o processing-java command line mode not working on Mac o https://github.com/processing/processing/issues/3409 X add build.xml prompt for OS X developers to download the JDK update X break out a gui package (get class count down in .app) +X move utility functions out of Base and into Util welcome X add "welcome" or "what's new" window to explain features in 3 @@ -117,6 +118,7 @@ _ https://github.com/processing/processing/issues/2953 beta +_ write notes about changes with Toolkit and Util, and packages _ write text for non-sketchbook version of welcome screen _ font fixes for Georgia in the examples _ CM ongoing notes and questions