Parse modern npm output; support ints, booleans and arrays of both in json parser (why oh why am I maintaining this?); support looking for libraries in all the places npm can hide them; parse javascript sources for require calls
1.1 --- a/nodejs/manifest.mf Wed Jun 27 13:13:01 2012 +0200
1.2 +++ b/nodejs/manifest.mf Sat Jun 30 02:53:09 2012 -0400
1.3 @@ -3,5 +3,5 @@
1.4 OpenIDE-Module-Layer: org/netbeans/modules/nodejs/layer.xml
1.5 OpenIDE-Module-Localizing-Bundle: org/netbeans/modules/nodejs/Bundle.properties
1.6 OpenIDE-Module-Requires: org.openide.modules.os.Unix
1.7 -OpenIDE-Module-Specification-Version: 1.15
1.8 +OpenIDE-Module-Specification-Version: 1.16
1.9
2.1 --- a/nodejs/src/org/netbeans/modules/nodejs/DefaultExectable.java Wed Jun 27 13:13:01 2012 +0200
2.2 +++ b/nodejs/src/org/netbeans/modules/nodejs/DefaultExectable.java Sat Jun 30 02:53:09 2012 -0400
2.3 @@ -274,12 +274,16 @@
2.4 Process p = b.start();
2.5 try {
2.6 InputStream in = p.getInputStream();
2.7 - p.waitFor();
2.8 - if (p.exitValue() == 0) {
2.9 - ByteArrayOutputStream out = new ByteArrayOutputStream();
2.10 - FileUtil.copy(in, out);
2.11 - String result = new String(out.toByteArray()).trim(); //trim off \n
2.12 - return result.length() == 0 ? null : result;
2.13 + try {
2.14 + p.waitFor();
2.15 + if (p.exitValue() == 0) {
2.16 + ByteArrayOutputStream out = new ByteArrayOutputStream();
2.17 + FileUtil.copy(in, out);
2.18 + String result = new String(out.toByteArray()).trim(); //trim off \n
2.19 + return result.length() == 0 ? null : result;
2.20 + }
2.21 + } finally {
2.22 + in.close();
2.23 }
2.24 } catch (InterruptedException ex) {
2.25 Exceptions.printStackTrace(ex);
3.1 --- a/nodejs/src/org/netbeans/modules/nodejs/NodeJSProject.java Wed Jun 27 13:13:01 2012 +0200
3.2 +++ b/nodejs/src/org/netbeans/modules/nodejs/NodeJSProject.java Sat Jun 30 02:53:09 2012 -0400
3.3 @@ -129,7 +129,7 @@
3.4 private final NodeJsClassPathProvider classpath = new NodeJsClassPathProvider();
3.5 private final PropertyChangeSupport supp = new PropertyChangeSupport(this);
3.6 private final Sources sources = new NodeJSProjectSources(this);
3.7 - private final Lookup lookup = Lookups.fixed(this, new NodeJSProjectProperties(this), classpath, sources);
3.8 + private final Lookup lookup = Lookups.fixed(this, new NodeJSProjectProperties(this), classpath, sources, new NodeJsEncodingQuery());
3.9
3.10 @SuppressWarnings("LeakingThisInConstructor")
3.11 NodeJSProject(FileObject dir, ProjectState state) {
4.1 --- a/nodejs/src/org/netbeans/modules/nodejs/NodeJSProjectProperties.java Wed Jun 27 13:13:01 2012 +0200
4.2 +++ b/nodejs/src/org/netbeans/modules/nodejs/NodeJSProjectProperties.java Sat Jun 30 02:53:09 2012 -0400
4.3 @@ -194,8 +194,16 @@
4.4 fo = project.getProjectDirectory().createData(".nbrun");
4.5 }
4.6 OutputStream out = fo.getOutputStream();
4.7 - PrintStream ps = new PrintStream(out);
4.8 - ps.println(args);
4.9 + try {
4.10 + PrintStream ps = new PrintStream(out);
4.11 + try {
4.12 + ps.println(args);
4.13 + } finally {
4.14 + ps.close();
4.15 + }
4.16 + } finally {
4.17 + out.close();
4.18 + }
4.19 } catch (IOException ex) {
4.20 Exceptions.printStackTrace(ex);
4.21 }
5.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
5.2 +++ b/nodejs/src/org/netbeans/modules/nodejs/NodeJsEncodingQuery.java Sat Jun 30 02:53:09 2012 -0400
5.3 @@ -0,0 +1,13 @@
5.4 +package org.netbeans.modules.nodejs;
5.5 +
5.6 +import java.nio.charset.Charset;
5.7 +import org.netbeans.spi.queries.FileEncodingQueryImplementation;
5.8 +import org.openide.filesystems.FileObject;
5.9 +
5.10 +public class NodeJsEncodingQuery extends FileEncodingQueryImplementation {
5.11 +
5.12 + @Override
5.13 + public Charset getEncoding(FileObject file) {
5.14 + return Charset.forName("UTF-8");
5.15 + }
5.16 +}
6.1 --- a/nodejs/src/org/netbeans/modules/nodejs/NodeProjectSourceNodeFactory.java Wed Jun 27 13:13:01 2012 +0200
6.2 +++ b/nodejs/src/org/netbeans/modules/nodejs/NodeProjectSourceNodeFactory.java Sat Jun 30 02:53:09 2012 -0400
6.3 @@ -45,17 +45,24 @@
6.4 import java.awt.EventQueue;
6.5 import java.awt.Image;
6.6 import java.awt.event.ActionEvent;
6.7 +import java.io.File;
6.8 import java.io.IOException;
6.9 import java.net.MalformedURLException;
6.10 import java.net.URL;
6.11 import java.util.ArrayList;
6.12 import java.util.Arrays;
6.13 import java.util.Collections;
6.14 +import java.util.Comparator;
6.15 +import java.util.HashMap;
6.16 import java.util.Iterator;
6.17 +import java.util.LinkedList;
6.18 import java.util.List;
6.19 import java.util.Map;
6.20 +import java.util.Set;
6.21 import java.util.logging.Level;
6.22 import java.util.logging.Logger;
6.23 +import java.util.regex.Matcher;
6.24 +import java.util.regex.Pattern;
6.25 import javax.swing.AbstractAction;
6.26 import javax.swing.Action;
6.27 import javax.swing.BorderFactory;
6.28 @@ -78,13 +85,16 @@
6.29 import org.openide.filesystems.FileUtil;
6.30 import org.openide.loaders.DataObject;
6.31 import org.openide.loaders.DataObjectNotFoundException;
6.32 +import org.openide.nodes.AbstractNode;
6.33 import org.openide.nodes.ChildFactory;
6.34 +import org.openide.nodes.Children;
6.35 import org.openide.nodes.FilterNode;
6.36 import org.openide.nodes.Node;
6.37 import org.openide.util.ChangeSupport;
6.38 import org.openide.util.Exceptions;
6.39 import org.openide.util.ImageUtilities;
6.40 import org.openide.util.NbBundle;
6.41 +import org.openide.util.NbCollections;
6.42 import org.openide.util.RequestProcessor;
6.43 import org.openide.windows.TopComponent;
6.44
6.45 @@ -97,6 +107,9 @@
6.46 private final ChangeSupport supp = new ChangeSupport(this);
6.47 private final Project project;
6.48
6.49 + private static final String[] builtInNodeLibs = new String[]{"assert", "buffer", "buffer_ieee754", "child_process", "cluster", "console", "constants", "crypto", "dgram", "dns", "events", "freelist", "fs", "http", "https", "module", "net", "os", "path", "punycode", "querystring", "readline", "repl", "stream", "string_decoder", "sys", "timers", "tls", "tty", "url", "util", "vm", "zlib"};
6.50 + public static final Pattern CHECK_FOR_REQUIRE = Pattern.compile("require\\s??\\(\\s??['\"](.*?)['\"]\\s??\\)", 40);
6.51 +
6.52 public NodeProjectSourceNodeFactory(Project p) {
6.53 this.project = p;
6.54 }
6.55 @@ -110,13 +123,18 @@
6.56 return new NodeProjectSourceNodeFactory(p);
6.57 }
6.58
6.59 - @Override
6.60 public List<Key> keys() {
6.61 List<Key> keys = new ArrayList<Key>();
6.62 -
6.63 +
6.64 FileObject libFolder = project.getProjectDirectory().getFileObject("node_modules");
6.65 VisibilityQuery q = VisibilityQuery.getDefault();
6.66 - for (FileObject fo : project.getProjectDirectory().getChildren()) {
6.67 + FileObject[] files = this.project.getProjectDirectory().getChildren();
6.68 + Arrays.sort(files, new FOComparator());
6.69 + List<FileObject> flds = new LinkedList();
6.70 + for (FileObject fo : files) {
6.71 + if (fo.equals(libFolder)) {
6.72 + continue;
6.73 + }
6.74 if (q.isVisible(fo)) {
6.75 if (fo.isData()) {
6.76 keys.add(new Key(KeyTypes.SOURCE, fo));
6.77 @@ -124,16 +142,20 @@
6.78 if (fo.getName().equals("package.json") || fo.equals(libFolder)) {
6.79 continue;
6.80 }
6.81 - keys.add(new Key(KeyTypes.SOURCE, fo));
6.82 + flds.add(fo);
6.83 }
6.84 }
6.85 }
6.86 - //now add libraries
6.87 + Map<String, List<FileObject>> otherLibs = findOtherModules(this.project.getProjectDirectory());
6.88 +
6.89 if (libFolder != null) {
6.90 - List<Key> libFolders = new ArrayList<Key>();
6.91 + List libFolders = new ArrayList();
6.92 for (FileObject lib : libFolder.getChildren()) {
6.93 boolean visible = q.isVisible(lib);
6.94 - if (visible && !"node_modules".equals(lib.getName()) && !"nbproject".equals(lib.getName()) && lib.isFolder()) {
6.95 + if ((visible) && (!"node_modules".equals(lib.getName())) && (!"nbproject".equals(lib.getName())) && (lib.isFolder())) {
6.96 + if (otherLibs.containsKey(lib.getName())) {
6.97 + otherLibs.remove(lib.getName());
6.98 + }
6.99 Key key = new Key(KeyTypes.LIBRARY, lib);
6.100 key.direct = true;
6.101 keys.add(key);
6.102 @@ -142,6 +164,73 @@
6.103 }
6.104 keys.addAll(libFolders);
6.105 }
6.106 + //XXX get this from "npm root"
6.107 + File userHomeModules = new File(new File(System.getProperty("user.home")), "node_modules");
6.108 + userHomeModules = (userHomeModules.exists()) && (userHomeModules.isDirectory()) ? userHomeModules : null;
6.109 +
6.110 + //XXX get this from "npm root -g"
6.111 + File libModules = new File("/usr/lib/node_modules");
6.112 + libModules = (libModules.exists()) && (libModules.isDirectory()) ? libModules : null;
6.113 +
6.114 + String src = DefaultExectable.get().getSourcesLocation();
6.115 + File nodeSources = src == null ? null : new File(src);
6.116 + File libDir = nodeSources == null ? null : new File(nodeSources, "lib");
6.117 +
6.118 + for (String lib : otherLibs.keySet()) {
6.119 + if ("./".equals(lib)) {
6.120 + continue;
6.121 + }
6.122 + if (userHomeModules != null) {
6.123 + File f = new File(userHomeModules, lib);
6.124 + if ((f.exists()) && (f.isDirectory())) {
6.125 + Key key = new Key(KeyTypes.LIBRARY, FileUtil.toFileObject(FileUtil.normalizeFile(f)));
6.126 + key.direct = true;
6.127 + keys.add(key);
6.128 + continue;
6.129 + }
6.130 + }
6.131 + if (libModules != null) {
6.132 + File f = new File(libModules, lib);
6.133 + if ((f.exists()) && (f.isDirectory())) {
6.134 + Key key = new Key(KeyTypes.LIBRARY, FileUtil.toFileObject(FileUtil.normalizeFile(f)));
6.135 + keys.add(key);
6.136 + continue;
6.137 + }
6.138 + }
6.139 + if (libDir != null) {
6.140 + File f = new File(libDir, lib + ".js");
6.141 + if ((f.exists()) && (f.isFile()) && (f.canRead())) {
6.142 + Key key = new Key(KeyTypes.LIBRARY, FileUtil.toFileObject(FileUtil.normalizeFile(f)));
6.143 + keys.add(key);
6.144 + continue;
6.145 + }
6.146 + }
6.147 + if (Arrays.binarySearch(builtInNodeLibs, lib) >= 0) {
6.148 + Key key = new NodeProjectSourceNodeFactory.Key.BuiltInLibrary(lib);
6.149 + keys.add(key);
6.150 + key.direct = true;
6.151 + continue;
6.152 + }
6.153 + Key.MissingLibrary key = new Key.MissingLibrary(lib);
6.154 + List<FileObject> referencedBy = otherLibs.get(lib);
6.155 + List<String> paths = new LinkedList<String>();
6.156 + for (FileObject fo : referencedBy) {
6.157 + if (FileUtil.isParentOf(project.getProjectDirectory(), fo)) {
6.158 + paths.add (FileUtil.getRelativePath(project.getProjectDirectory(), fo));
6.159 + } else {
6.160 + paths.add(fo.getPath());
6.161 + }
6.162 + }
6.163 + key.references = paths;
6.164 + keys.add(key);
6.165 + }
6.166 +
6.167 + for (FileObject fo : flds) {
6.168 + if (fo.getName().equals("node_modules")) {
6.169 + continue;
6.170 + }
6.171 + keys.add(new Key(KeyTypes.SOURCE, fo));
6.172 + }
6.173 return keys;
6.174 }
6.175
6.176 @@ -150,7 +239,7 @@
6.177 if (libs != null) {
6.178 for (FileObject fo : libFolder.getChildren()) {
6.179 for (FileObject lib : fo.getChildren()) {
6.180 - if (!"node_modules".equals(lib.getName()) && !"nbproject".equals(lib.getName()) && lib.isFolder()) {
6.181 + if ((!"node_modules".equals(lib.getName())) && (!"nbproject".equals(lib.getName())) && (lib.isFolder())) {
6.182 boolean jsFound = false;
6.183 for (FileObject kid : lib.getChildren()) {
6.184 jsFound = "js".equals(kid.getExt());
6.185 @@ -170,6 +259,35 @@
6.186 }
6.187 }
6.188
6.189 + private Map<String, List<FileObject>> findOtherModules(FileObject fld) {
6.190 + Map<String, List<FileObject>> libs = new HashMap<String,List<FileObject>>();
6.191 + assert (!EventQueue.isDispatchThread());
6.192 + for (FileObject fo : NbCollections.iterable(fld.getChildren(true))) {
6.193 + if (("js".equals(fo.getExt())) && (fo.isData()) && (fo.canRead())) {
6.194 + checkForLibraries(fo, libs);
6.195 + }
6.196 + }
6.197 + return libs;
6.198 + }
6.199 +
6.200 + private void checkForLibraries(FileObject jsFile,Map<String, List<FileObject>> all) {
6.201 + try {
6.202 + String text = jsFile.asText();
6.203 + Matcher m = CHECK_FOR_REQUIRE.matcher(text);
6.204 + while (m.find()) {
6.205 +// all.add(m.group(1));
6.206 + List<FileObject> l = all.get(m.group(1));
6.207 + if (l == null) {
6.208 + l = new LinkedList<FileObject>();
6.209 + all.put(m.group(1), l);
6.210 + }
6.211 + l.add(jsFile);
6.212 + }
6.213 + } catch (IOException ex) {
6.214 + Logger.getLogger(NodeProjectSourceNodeFactory.class.getName()).log(Level.INFO, jsFile.getPath(), ex);
6.215 + }
6.216 + }
6.217 +
6.218 @Override
6.219 public void addChangeListener(ChangeListener l) {
6.220 supp.addChangeListener(l);
6.221 @@ -181,12 +299,39 @@
6.222 }
6.223
6.224 @Override
6.225 - public Node node(Key key) {
6.226 + public Node node(final Key key) {
6.227 switch (key.type) {
6.228 case LIBRARY:
6.229 return new LibraryFilterNode(key);
6.230 case SOURCE:
6.231 return new FilterNode(nodeFromKey(key));
6.232 + case BUILT_IN_LIBRARY:
6.233 + AbstractNode li = new AbstractNode(Children.LEAF);
6.234 + li.setName(key.toString());
6.235 + li.setDisplayName(key.toString());
6.236 + li.setShortDescription("Built-in library '" + key + "'");
6.237 + li.setIconBaseWithExtension("org/netbeans/modules/nodejs/resources/libs.png"); //NOI18N
6.238 + return li;
6.239 + case MISSING_LIBRARY:
6.240 + AbstractNode an = new AbstractNode(Children.LEAF) {
6.241 + @Override
6.242 + public String getHtmlDisplayName() {
6.243 + return "<font color=\"#EE0000\">" + key; //NOI18N
6.244 + }
6.245 + };
6.246 + an.setName(key.toString());
6.247 + an.setDisplayName(key.toString());
6.248 + StringBuilder sb = new StringBuilder("<html>Missing library <b><i>" + key + "</i></b>");
6.249 + if (key instanceof Key.MissingLibrary && ((Key.MissingLibrary) key).references != null && !((Key.MissingLibrary) key).references.isEmpty()) {
6.250 + sb.append("<p>Referenced By<br><ul>");
6.251 + for (String path : ((Key.MissingLibrary) key).references) {
6.252 + sb.append("<li>").append(path).append("</li>\n");
6.253 + }
6.254 + sb.append("</ul></pre></blockquote></html>");
6.255 + }
6.256 + an.setShortDescription(sb.toString());
6.257 + an.setIconBaseWithExtension("org/netbeans/modules/nodejs/resources/libs.png");
6.258 + return an;
6.259 default:
6.260 throw new AssertionError();
6.261 }
6.262 @@ -232,7 +377,7 @@
6.263 //do nothing
6.264 }
6.265
6.266 - static final class Key {
6.267 + static class Key {
6.268
6.269 private final KeyTypes type;
6.270 private final FileObject fld;
6.271 @@ -244,14 +389,43 @@
6.272 }
6.273
6.274 public String toString() {
6.275 - return type + " " + fld.getName() + (direct ? " direct" : " indirect");
6.276 + return type + " " + fld.getName() + (direct ? " direct" : " indirect"); //NOI18N
6.277 }
6.278 +
6.279 + static class BuiltInLibrary extends Key {
6.280 + private final String name;
6.281 + BuiltInLibrary(String name) {
6.282 + super (KeyTypes.BUILT_IN_LIBRARY, null);
6.283 + this.name = name;
6.284 + }
6.285 +
6.286 + @Override
6.287 + public String toString() {
6.288 + return name;
6.289 + }
6.290 + }
6.291 +
6.292 + static class MissingLibrary extends Key {
6.293 + private final String name;
6.294 + private List<String> references;
6.295 + MissingLibrary(String name) {
6.296 + super (KeyTypes.MISSING_LIBRARY, null);
6.297 + this.name = name;
6.298 + }
6.299 +
6.300 + @Override
6.301 + public String toString() {
6.302 + return name;
6.303 + }
6.304 + }
6.305 +
6.306 }
6.307
6.308 static enum KeyTypes {
6.309 -
6.310 SOURCE,
6.311 - LIBRARY
6.312 + LIBRARY,
6.313 + BUILT_IN_LIBRARY,
6.314 + MISSING_LIBRARY
6.315 }
6.316
6.317 interface LibrariesFolderFinder {
6.318 @@ -431,13 +605,13 @@
6.319 public String getHtmlDisplayName() {
6.320 StringBuilder sb = new StringBuilder();
6.321 if (!key.direct) {
6.322 - sb.append("<font color='!controlShadow'>");
6.323 + sb.append("<font color='!controlDkShadow'>");
6.324 }
6.325 sb.append(getDisplayName());
6.326 if (version != null) {
6.327 sb.append(" <i><font color='#9999AA'> ").append(version).append("</i>");
6.328 if (!key.direct) {
6.329 - sb.append("<font color='!controlShadow'>");
6.330 + sb.append("<font color='!controlDkShadow'>");
6.331 }
6.332 }
6.333 if (!key.direct) {
6.334 @@ -683,4 +857,30 @@
6.335 //do nothing
6.336 }
6.337 }
6.338 + private static class FOComparator
6.339 + implements Comparator<FileObject> {
6.340 +
6.341 + public int compare(FileObject o1, FileObject o2) {
6.342 + boolean aJs = ("js".equals(o1.getExt())) || ("json".equals(o1.getExt()));
6.343 + boolean bJs = ("js".equals(o2.getExt())) || ("json".equals(o2.getExt()));
6.344 +
6.345 + boolean aFld = o1.isFolder();
6.346 + boolean bFld = o2.isFolder();
6.347 +
6.348 + if (aJs == bJs) {
6.349 + if (aFld == bFld) {
6.350 + return o1.getName().compareToIgnoreCase(o2.getName());
6.351 + }
6.352 + if (aFld) {
6.353 + return 1;
6.354 + }
6.355 + return -1;
6.356 + }
6.357 +
6.358 + if (aJs) {
6.359 + return -1;
6.360 + }
6.361 + return 1;
6.362 + }
6.363 + }
6.364 }
7.1 --- a/nodejs/src/org/netbeans/modules/nodejs/ProjectMetadataImpl.java Wed Jun 27 13:13:01 2012 +0200
7.2 +++ b/nodejs/src/org/netbeans/modules/nodejs/ProjectMetadataImpl.java Sat Jun 30 02:53:09 2012 -0400
7.3 @@ -45,6 +45,7 @@
7.4 import java.beans.PropertyChangeSupport;
7.5 import java.io.ByteArrayInputStream;
7.6 import java.io.IOException;
7.7 +import java.io.InputStream;
7.8 import java.io.OutputStream;
7.9 import java.util.ArrayList;
7.10 import java.util.Arrays;
7.11 @@ -67,7 +68,6 @@
7.12 import org.openide.filesystems.FileAlreadyLockedException;
7.13 import org.openide.filesystems.FileChangeAdapter;
7.14 import org.openide.filesystems.FileEvent;
7.15 -import org.openide.filesystems.FileLock;
7.16 import org.openide.filesystems.FileObject;
7.17 import org.openide.filesystems.FileSystem.AtomicAction;
7.18 import org.openide.filesystems.FileUtil;
7.19 @@ -179,21 +179,23 @@
7.20 return map;
7.21 }
7.22 }
7.23 - FileLock fileLock = fo.lock();
7.24 try {
7.25 SimpleJSONParser p = new SimpleJSONParser(true); //permissive mode - will parse as much as it can
7.26 - Map<String, Object> m = p.parse(fo);
7.27 - ProjectMetadataImpl.this.hasErrors = err = p.hasErrors();
7.28 - synchronized (this) {
7.29 - map = Collections.synchronizedMap(m);
7.30 - return map;
7.31 + InputStream in = fo.getInputStream();
7.32 + try {
7.33 + Map<String, Object> m = p.parse(fo);
7.34 + ProjectMetadataImpl.this.hasErrors = err = p.hasErrors();
7.35 + synchronized (this) {
7.36 + map = Collections.synchronizedMap(m);
7.37 + return map;
7.38 + }
7.39 + } finally {
7.40 + in.close();
7.41 }
7.42 } catch (JsonException ex) {
7.43 Logger.getLogger(ProjectMetadataImpl.class.getName()).log(Level.INFO,
7.44 "Bad package.json in " + fo.getPath(), ex);
7.45 return new LinkedHashMap<String, Object>();
7.46 - } finally {
7.47 - fileLock.releaseLock();
7.48 }
7.49 } finally {
7.50 lock.unlock();
7.51 @@ -203,7 +205,7 @@
7.52 }
7.53 }
7.54
7.55 - private final Map<String, Object> getMap() {
7.56 + public final Map<String, Object> getMap() {
7.57 Map<String, Object> result = map;
7.58 if (result == null) {
7.59 synchronized (this) {
7.60 @@ -212,7 +214,9 @@
7.61 }
7.62 if (result == null) {
7.63 final FileObject fo = project.getProjectDirectory().getFileObject("package.json");
7.64 + System.out.println("GOT FO " + fo);
7.65 if (fo == null) {
7.66 + System.err.println("No package.json");
7.67 return new LinkedHashMap<String, Object>();
7.68 }
7.69 if (!listening) {
7.70 @@ -220,12 +224,13 @@
7.71 fo.addFileChangeListener(FileUtil.weakFileChangeListener(this, fo));
7.72 }
7.73 try {
7.74 + System.out.println("LOAD " + fo);
7.75 result = load(fo);
7.76 synchronized (this) {
7.77 map = result;
7.78 }
7.79 } catch (IOException ioe) {
7.80 - Logger.getLogger(ProjectMetadataImpl.class.getName()).log(Level.INFO,
7.81 + Logger.getLogger(ProjectMetadataImpl.class.getName()).log(Level.WARNING,
7.82 "Problems loading " + fo.getPath(), ioe);
7.83 result = new LinkedHashMap<String,Object>();
7.84 }
8.1 --- a/nodejs/src/org/netbeans/modules/nodejs/json/SimpleJSONParser.java Wed Jun 27 13:13:01 2012 +0200
8.2 +++ b/nodejs/src/org/netbeans/modules/nodejs/json/SimpleJSONParser.java Sat Jun 30 02:53:09 2012 -0400
8.3 @@ -107,11 +107,11 @@
8.4 }
8.5
8.6 public Map<String, Object> parse(FileObject in) throws JsonException, IOException {
8.7 - return parse(in.asText());
8.8 + return parse(in.asText("UTF-8"));
8.9 }
8.10
8.11 public Map<String, Object> parse(InputStream in) throws JsonException, IOException {
8.12 - return parse(in, null);
8.13 + return parse(in, "UTF-8");
8.14 }
8.15
8.16 public Map<String, Object> parse(InputStream in, String encoding) throws JsonException, IOException {
8.17 @@ -139,15 +139,15 @@
8.18 private final class CharVisitor {
8.19
8.20 StringBuilder sb = new StringBuilder();
8.21 - S s = BEGIN;
8.22 - Stack<S> awaits = new Stack<S>();
8.23 + S s = S.BEGIN;
8.24 + Stack<S> awaits = new Stack();
8.25 + char lastChar;
8.26 + S stateBeforeComment;
8.27
8.28 void setState(S s, char c, int pos) {
8.29 stateChange(this.s, s, c, pos);
8.30 this.s = s;
8.31 }
8.32 - char lastChar;
8.33 - S stateBeforeComment;
8.34
8.35 void visitChar(char c, int pos, int line, State state) throws JsonException {
8.36 if (c == '/' && s != IN_ARRAY_ELEMENT && s != IN_KEY && s != IN_VALUE && s != AWAIT_BEGIN_COMMENT && s != IN_COMMENT && s != IN_LINE_COMMENT) {
8.37 @@ -274,6 +274,26 @@
8.38 setState(AWAITING_COMPOUND_VALUE, c, pos);
8.39 state.enterCompoundValue();
8.40 break;
8.41 + case 'f':
8.42 + case 't':
8.43 + sb.append(c);
8.44 + setState(IN_BOOLEAN_VALUE, c, pos);
8.45 + break;
8.46 + case '-':
8.47 + case '.':
8.48 + case '0':
8.49 + case '1':
8.50 + case '2':
8.51 + case '3':
8.52 + case '4':
8.53 + case '5':
8.54 + case '6':
8.55 + case '7':
8.56 + case '8':
8.57 + case '9':
8.58 + sb.append(c);
8.59 + setState(IN_NUMERIC_VALUE, c, pos);
8.60 + break;
8.61 default:
8.62 error("Expected '\"' or ':' to start value", c, line, pos);
8.63 }
8.64 @@ -294,6 +314,26 @@
8.65 case '"':
8.66 setState(S.IN_ARRAY_ELEMENT, c, pos);
8.67 break;
8.68 + case 'f':
8.69 + case 't':
8.70 + setState(IN_BOOLEAN_ARRAY_ELEMENT, c, pos);
8.71 + sb.append(c);
8.72 + break;
8.73 + case '-':
8.74 + case '.':
8.75 + case '0':
8.76 + case '1':
8.77 + case '2':
8.78 + case '3':
8.79 + case '4':
8.80 + case '5':
8.81 + case '6':
8.82 + case '7':
8.83 + case '8':
8.84 + case '9':
8.85 + setState(IN_NUMERIC_ARRAY_ELEMENT, c, pos);
8.86 + sb.append(c);
8.87 + break;
8.88 case ']':
8.89 setState(AFTER_VALUE, c, pos);
8.90 state.exitArrayValue();
8.91 @@ -311,6 +351,124 @@
8.92 }
8.93 sb.append(c);
8.94 break;
8.95 + case IN_NUMERIC_ARRAY_ELEMENT:
8.96 + if ((Character.isWhitespace(c)) || (c == ',') || (c == ']')) {
8.97 + if (sb.length() > 0) {
8.98 + state.numericArrayElement(sb.toString());
8.99 + sb.setLength(0);
8.100 + }
8.101 + if (Character.isWhitespace(c)) {
8.102 + setState(AFTER_ARRAY_ELEMENT, c, pos);
8.103 + } else {
8.104 + setState(c == ']' ? AFTER_VALUE : AWAITING_ARRAY_ELEMENT, c, pos);
8.105 + if (c == ']') {
8.106 + state.exitArrayValue();
8.107 + }
8.108 + }
8.109 + return;
8.110 + }
8.111 + if ((c != '.') && (c != '-') && (!Character.isDigit(c))) {
8.112 + error("Invalid character in numeric array element: ", c, line, pos);
8.113 + } else {
8.114 + sb.append(c);
8.115 + }
8.116 +
8.117 + break;
8.118 + case IN_BOOLEAN_ARRAY_ELEMENT:
8.119 + if ((Character.isWhitespace(c)) || (c == ',') || (c == ']')) {
8.120 + if (sb.length() > 0) {
8.121 + state.booleanArrayElement(sb.toString());
8.122 + sb.setLength(0);
8.123 + }
8.124 + if (Character.isWhitespace(c)) {
8.125 + setState(AFTER_ARRAY_ELEMENT, c, pos);
8.126 + } else {
8.127 + setState(c == ']' ? AFTER_VALUE : AWAITING_ARRAY_ELEMENT, c, pos);
8.128 + if (c == ']') {
8.129 + state.exitArrayValue();
8.130 + }
8.131 + }
8.132 + return;
8.133 + }
8.134 + if ((!"true".startsWith(sb.toString())) && (!"false".startsWith(sb.toString()))) {
8.135 + error("Invalid character in boolean array element for '" + this.sb + "': ", c, line, pos);
8.136 + } else {
8.137 + sb.append(c);
8.138 + }
8.139 +
8.140 + break;
8.141 + case IN_NUMERIC_VALUE:
8.142 + if ((Character.isWhitespace(c)) || (c == ',')) {
8.143 + setState(AWAITING_KEY, c, pos);
8.144 + state.numberValue(sb.toString());
8.145 + sb.setLength(0);
8.146 + return;
8.147 + }
8.148 + if ((Character.isDigit(c)) || (c == '.') || (c == '-')) {
8.149 + if ((sb.indexOf(".") >= 0) && (c == '.')) {
8.150 + error("Extra decimal in number: ", c, line, pos);
8.151 + } else {
8.152 + sb.append(c);
8.153 + }
8.154 + } else {
8.155 + error("Invalid character in number: ", c, line, pos);
8.156 + }
8.157 + break;
8.158 + case IN_BOOLEAN_VALUE:
8.159 + if ((Character.isWhitespace(c)) || (c == ',')) {
8.160 + setState(AWAITING_KEY, c, pos);
8.161 + state.booleanValue(sb.toString());
8.162 + sb.setLength(0);
8.163 + return;
8.164 + }
8.165 + char lc = sb.length() == 0 ? '\000' : sb.charAt(sb.length() - 1);
8.166 + switch (c) {
8.167 + case 'r':
8.168 + if (lc != 't') {
8.169 + error("Invalid character in boolean - lc=" + lc + ": " + this.sb, c, line, pos);
8.170 + } else {
8.171 + sb.append(c);
8.172 + }
8.173 + break;
8.174 + case 'u':
8.175 + if (lc != 'r') {
8.176 + error("Invalid character in boolean: " + this.sb + " lc is " + lc + " - ", c, line, pos);
8.177 + } else {
8.178 + sb.append(c);
8.179 + }
8.180 + break;
8.181 + case 'e':
8.182 + if ((lc != 'u') && (lc != 's')) {
8.183 + error("Invalid character in boolean: ", c, line, pos);
8.184 + } else {
8.185 + sb.append(c);
8.186 + }
8.187 + break;
8.188 + case 'a':
8.189 + if (lc != 'f') {
8.190 + error("Invalid character in boolean: ", c, line, pos);
8.191 + } else {
8.192 + sb.append(c);
8.193 + }
8.194 + break;
8.195 + case 'l':
8.196 + if (lc != 'a') {
8.197 + error("Invalid character in boolean: ", c, line, pos);
8.198 + } else {
8.199 + sb.append(c);
8.200 + }
8.201 + break;
8.202 + case 's':
8.203 + if (lc != 'l') {
8.204 + error("Invalid character in boolean: ", c, line, pos);
8.205 + } else {
8.206 + sb.append(c);
8.207 + }
8.208 + break;
8.209 + default:
8.210 + error("Invalid character in boolean: ", c, line, pos);
8.211 + }
8.212 + break;
8.213 case IN_VALUE:
8.214 if (c == '"') {
8.215 setState(S.AFTER_VALUE, c, pos);
8.216 @@ -405,18 +563,21 @@
8.217 }
8.218
8.219 private void stateChange(S s, S to, char c, int pos) {
8.220 -// System.out.println("from " + s + " to " + to + " " + c + " at " + pos);
8.221 }
8.222 }
8.223
8.224 - enum S {
8.225 + public enum S {
8.226
8.227 AWAIT_BEGIN_COMMENT,
8.228 IN_COMMENT,
8.229 IN_LINE_COMMENT,
8.230 IN_KEY,
8.231 IN_VALUE,
8.232 + IN_NUMERIC_VALUE,
8.233 + IN_BOOLEAN_VALUE,
8.234 IN_ARRAY_ELEMENT,
8.235 + IN_BOOLEAN_ARRAY_ELEMENT,
8.236 + IN_NUMERIC_ARRAY_ELEMENT,
8.237 BEGIN,
8.238 AWAITING_KEY,
8.239 AWAITING_COMPOUND_VALUE,
8.240 @@ -469,6 +630,76 @@
8.241 }
8.242 return AWAITING_KEY;
8.243 }
8.244 +
8.245 +
8.246 + private void numericArrayElement(String value) {
8.247 + try {
8.248 + String key = (String) this.currKey.peek();
8.249 + List l = null;
8.250 + if ((l == null) && (!this.currList.isEmpty())) {
8.251 + l = (List) this.currList.peek();
8.252 + } else if (l == null) {
8.253 + throw new SimpleJSONParser.Internal("No array present for array value " + value);
8.254 + }
8.255 + l.add(toNumber(value));
8.256 + } catch (NumberFormatException nfe) {
8.257 + throw new SimpleJSONParser.Internal("Bad number '" + value + "'");
8.258 + }
8.259 + }
8.260 +
8.261 + private void booleanArrayElement(String value) {
8.262 + String key = (String) this.currKey.peek();
8.263 + List l = null;
8.264 + if ((l == null) && (!this.currList.isEmpty())) {
8.265 + l = (List) this.currList.peek();
8.266 + } else if (l == null) {
8.267 + throw new SimpleJSONParser.Internal("No array present for array value " + value);
8.268 + }
8.269 + if ("true".equals(value)) {
8.270 + l.add(Boolean.valueOf(true));
8.271 + } else if ("false".equals(value)) {
8.272 + l.add(Boolean.valueOf(false));
8.273 + } else {
8.274 + throw new SimpleJSONParser.Internal("Illegal boolean value '" + value + "'");
8.275 + }
8.276 + }
8.277 +
8.278 + private void booleanValue(String s) {
8.279 + if ("true".equals(s)) {
8.280 + String key = (String) this.currKey.pop();
8.281 + this.curr.put(key, Boolean.TRUE);
8.282 + } else if ("false".equals(s)) {
8.283 + String key = (String) this.currKey.pop();
8.284 + this.curr.put(key, Boolean.FALSE);
8.285 + } else {
8.286 + throw new SimpleJSONParser.Internal("Invalid boolean '" + s + "'");
8.287 + }
8.288 + }
8.289 +
8.290 + private Number toNumber(String toString) {
8.291 + Number n;
8.292 + if (toString.indexOf(".") >= 0) {
8.293 + n = Double.valueOf(Double.parseDouble(toString));
8.294 + if (n.floatValue() == n.doubleValue()) {
8.295 + n = Float.valueOf(n.floatValue());
8.296 + }
8.297 + } else {
8.298 + n = Long.valueOf(Long.parseLong(toString));
8.299 + if (n.longValue() == n.intValue()) {
8.300 + n = Integer.valueOf(n.intValue());
8.301 + }
8.302 + }
8.303 + return n;
8.304 + }
8.305 +
8.306 + private void numberValue(String toString) {
8.307 + try {
8.308 + String key = (String) this.currKey.pop();
8.309 + this.curr.put(key, toNumber(toString));
8.310 + } catch (NumberFormatException nfe) {
8.311 + throw new SimpleJSONParser.Internal("Invalid number '" + toString + "'");
8.312 + }
8.313 + }
8.314
8.315 public void enterCompoundValue() {
8.316 String key = currKey.isEmpty() ? null : currKey.peek();
8.317 @@ -543,16 +774,29 @@
8.318 Arrays.fill(indentChars, ' ');
8.319 String ind = new String(indentChars);
8.320 String indl = ind + " ";
8.321 - sb.append('\n').append(ind).append('[').append('\n');
8.322 - for (Iterator<Object> it = l.iterator(); it.hasNext();) {
8.323 + boolean inline = l.isEmpty() || l.get(0) instanceof Boolean || l.get(0) instanceof Number;
8.324 + if (!inline) {
8.325 + sb.append('\n').append(ind);
8.326 + }
8.327 + sb.append('[');
8.328 + if (!inline) {
8.329 + sb.append('\n');
8.330 + }
8.331 + int ix = 0;
8.332 + for (Iterator it = l.iterator(); it.hasNext();) {
8.333 Object o = it.next();
8.334 if (o instanceof Map) {
8.335 Map<String, Object> mm = (Map<String, Object>) o;
8.336 out(mm, sb, indent + 1);
8.337 } else if (o instanceof List) {
8.338 out((List) o, sb, indent + 1);
8.339 - } else if (o instanceof CharSequence) {
8.340 - String s = ("" + o).replace("\"", "\\\"");
8.341 + } else if (((o instanceof Number)) || ((o instanceof Boolean))) {
8.342 + sb.append(o);
8.343 + if (it.hasNext()) {
8.344 + sb.append(',');
8.345 + }
8.346 + } else if ((o instanceof CharSequence)) {
8.347 + String s = new StringBuilder().append("").append(o).toString().replace("\"", "\\\"");
8.348 sb.append(indl).append('"').append(s).append('"');
8.349 if (it.hasNext()) {
8.350 sb.append(',');
8.351 @@ -569,8 +813,12 @@
8.352 reflectOut(o, sb, indent + 1);
8.353 }
8.354 }
8.355 + ix++;
8.356 }
8.357 - sb.append(ind).append(']');
8.358 + if (!inline) {
8.359 + sb.append(ind);
8.360 + }
8.361 + sb.append(']');
8.362 }
8.363
8.364 private static final void reflectOut(Object o, StringBuilder sb, int indent) {
8.365 @@ -657,6 +905,8 @@
8.366 out((Map<String, Object>) e.getValue(), sb, indent + 1);
8.367 sb.append(ind);
8.368 sb.append('}');
8.369 + } else if (((e.getValue() instanceof Number)) || ((e.getValue() instanceof Boolean))) {
8.370 + sb.append(e.getValue());
8.371 } else if (e.getValue().getClass().isArray()) {
8.372 if (e.getValue().getClass().getComponentType().isPrimitive()) {
8.373 Object[] o = Utilities.toObjectArray(e.getValue());
9.1 --- a/nodejs/src/org/netbeans/modules/nodejs/libraries/LibrariesPanel.java Wed Jun 27 13:13:01 2012 +0200
9.2 +++ b/nodejs/src/org/netbeans/modules/nodejs/libraries/LibrariesPanel.java Sat Jun 30 02:53:09 2012 -0400
9.3 @@ -378,9 +378,9 @@
9.4 }
9.5 }
9.6 }
9.7 - private static final Pattern p = Pattern.compile(
9.8 - "(\\S+)\\s+(.*)=(\\S+)"); //NOI18N
9.9 -
9.10 + public static final Pattern p = Pattern.compile(
9.11 + "^(\\S+)\\s+(.*?)=(\\S+).*?$", Pattern.MULTILINE | Pattern.DOTALL); //NOI18N
9.12 +
9.13 private void publish(CharSequence seq) {
9.14 final Matcher m = p.matcher(seq);
9.15 Process p;
10.1 --- a/nodejs/test/unit/src/org/netbeans/modules/nodejs/json/MetadataTest.java Wed Jun 27 13:13:01 2012 +0200
10.2 +++ b/nodejs/test/unit/src/org/netbeans/modules/nodejs/json/MetadataTest.java Sat Jun 30 02:53:09 2012 -0400
10.3 @@ -40,12 +40,15 @@
10.4 * Portions Copyrighted 2011 Sun Microsystems, Inc.
10.5 */
10.6 package org.netbeans.modules.nodejs.json;
10.7 +import java.io.IOException;
10.8 +import java.io.InputStream;
10.9 +import java.io.OutputStream;
10.10 +import java.util.Map;
10.11 import org.netbeans.modules.nodejs.ProjectMetadataImpl;
10.12
10.13 import org.junit.Test;
10.14 import static org.junit.Assert.*;
10.15 import org.netbeans.api.project.Project;
10.16 -import org.netbeans.modules.nodejs.ProjectMetadata;
10.17 import org.openide.filesystems.FileObject;
10.18 import org.openide.filesystems.FileUtil;
10.19 import org.openide.util.Lookup;
10.20 @@ -56,9 +59,20 @@
10.21 */
10.22 public class MetadataTest {
10.23 Fake fake = new Fake();
10.24 - ProjectMetadata impl = new ProjectMetadataImpl(fake);
10.25 + ProjectMetadataImpl impl = new ProjectMetadataImpl(fake);
10.26
10.27 @Test
10.28 + public void testLoading() {
10.29 + Map m = impl.getMap();
10.30 + System.out.println("GOT " + m);
10.31 + assertNotNull(m);
10.32 + assertFalse(m.isEmpty());
10.33 + assertEquals("recon", impl.getValue("name"));
10.34 + assertEquals("0.0.8", impl.getValue("version"));
10.35 + assertEquals("git", impl.getValue("repository.type"));
10.36 + }
10.37 +
10.38 + @Test
10.39 public void test() {
10.40 impl.setValue("name", "thing");
10.41 assertEquals ("thing", impl.getValue("name"));
10.42 @@ -77,6 +91,25 @@
10.43
10.44 static class Fake implements Project {
10.45 FileObject root = FileUtil.createMemoryFileSystem().getRoot();
10.46 +
10.47 + Fake() {
10.48 + try {
10.49 + InputStream in = MetadataTest.class.getResourceAsStream("package_0.json");
10.50 + try {
10.51 + FileObject fo = root.createData("package.json");
10.52 + OutputStream out = fo.getOutputStream();
10.53 + try {
10.54 + FileUtil.copy(in, out);
10.55 + } finally {
10.56 + out.close();
10.57 + }
10.58 + } finally {
10.59 + in.close();
10.60 + }
10.61 + } catch (IOException ex) {
10.62 + throw new Error(ex);
10.63 + }
10.64 + }
10.65
10.66 public FileObject getProjectDirectory() {
10.67 return root;
11.1 --- a/nodejs/test/unit/src/org/netbeans/modules/nodejs/json/SimpleJSONParserTest.java Wed Jun 27 13:13:01 2012 +0200
11.2 +++ b/nodejs/test/unit/src/org/netbeans/modules/nodejs/json/SimpleJSONParserTest.java Sat Jun 30 02:53:09 2012 -0400
11.3 @@ -44,6 +44,7 @@
11.4 import java.io.IOException;
11.5 import java.util.Map;
11.6 import java.io.InputStream;
11.7 +import java.util.List;
11.8 import org.junit.Test;
11.9 import static org.junit.Assert.*;
11.10 import org.netbeans.modules.nodejs.json.SimpleJSONParser.JsonException;
11.11 @@ -56,7 +57,7 @@
11.12
11.13 @Test
11.14 public void testParse() throws IOException, JsonException {
11.15 - for (int i = 0; i < 10; i++) {
11.16 + for (int i = 0; i < 11; i++) {
11.17 parseJSON("package_" + i + ".json");
11.18 }
11.19 for (int i = 0; i < 4; i++) {
11.20 @@ -68,6 +69,24 @@
11.21 }
11.22 }
11.23 }
11.24 +
11.25 + @Test
11.26 + public void testIntAndBool() throws Exception {
11.27 + String t = "{ \"foo\": 23, \"bar\": true, \"baz\" : [5,10,15,20], \"quux\": [true,false,false,true] }";
11.28 + Map<String,Object> m = new SimpleJSONParser().parse(t);
11.29 + assertNotNull(m.get("foo"));
11.30 + assertNotNull(m.get("bar"));
11.31 + assertNotNull(m.get("baz"));
11.32 + assertNotNull(m.get("quux"));
11.33 + assertTrue (m.get("foo") instanceof Integer);
11.34 + assertTrue (m.get("bar") instanceof Boolean);
11.35 + assertTrue (m.get("baz") instanceof List);
11.36 + assertTrue (m.get("baz") instanceof List);
11.37 +
11.38 + CharSequence nue = new SimpleJSONParser().toJSON(m);
11.39 + Map<String,Object> m1 = new SimpleJSONParser().parse(nue);
11.40 + assertEquals(m, m1);
11.41 + }
11.42
11.43 private void parseJSON(String what) throws IOException, JsonException {
11.44 System.out.println("-------------------------------");
12.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
12.2 +++ b/nodejs/test/unit/src/org/netbeans/modules/nodejs/json/package_10.json Sat Jun 30 02:53:09 2012 -0400
12.3 @@ -0,0 +1,35 @@
12.4 +{
12.5 + "description" : "A thing to test flatiron",
12.6 + "version" : "0.0.0",
12.7 + "flah" : true,
12.8 + "fbooger" : [12,3,4,false],
12.9 + "dependencies" : {
12.10 + "union" : "0.3.0",
12.11 + "flatiron" : "0.2.2"
12.12 + },
12.13 + "devDependencies" : {
12.14 + "api-easy" : "0.3.2",
12.15 + "vows" : "0.6.1"
12.16 + },
12.17 + "scripts" : {
12.18 + "test" : "vows --spec",
12.19 + "start" : "node app.js"
12.20 + },
12.21 + "name" : "doohickey",
12.22 + "author" : {
12.23 + "name" : "Tim Boudreau",
12.24 + "email" : "niftiness@gmail.com"
12.25 + },
12.26 + "homepage" : "http://timboudreau.com",
12.27 + "keywords" :
12.28 + [
12.29 + "food"
12.30 + ],
12.31 + "bugs" : {
12.32 + "web" : "null"
12.33 + },
12.34 + "main" : "app.js",
12.35 + "license" : {
12.36 + "type" : "bsd"
12.37 + }
12.38 +}