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
authorTim Boudreau <tboudreau@netbeans.org>
Sat, 30 Jun 2012 02:53:09 -0400
changeset 17844d655b38006a7
parent 17843 5ee1ca55870e
child 17845 5d62c6245fd7
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
nodejs/manifest.mf
nodejs/src/org/netbeans/modules/nodejs/DefaultExectable.java
nodejs/src/org/netbeans/modules/nodejs/NodeJSProject.java
nodejs/src/org/netbeans/modules/nodejs/NodeJSProjectProperties.java
nodejs/src/org/netbeans/modules/nodejs/NodeJsEncodingQuery.java
nodejs/src/org/netbeans/modules/nodejs/NodeProjectSourceNodeFactory.java
nodejs/src/org/netbeans/modules/nodejs/ProjectMetadataImpl.java
nodejs/src/org/netbeans/modules/nodejs/json/SimpleJSONParser.java
nodejs/src/org/netbeans/modules/nodejs/libraries/LibrariesPanel.java
nodejs/test/unit/src/org/netbeans/modules/nodejs/json/MetadataTest.java
nodejs/test/unit/src/org/netbeans/modules/nodejs/json/SimpleJSONParserTest.java
nodejs/test/unit/src/org/netbeans/modules/nodejs/json/package_10.json
     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 +}