Support for more advanced UIs (web and non-web).
authorJan Lahoda <jlahoda@netbeans.org>
Sun, 10 Feb 2013 14:51:55 +0100
changeset 9280a69f6ca9f2c
parent 927 fa494946f556
child 929 57dc42a0fcd6
Support for more advanced UIs (web and non-web).
remoting/server/web/web.ui/src/org/netbeans/modules/jackpot30/backend/ui/UI.java
     1.1 --- a/remoting/server/web/web.ui/src/org/netbeans/modules/jackpot30/backend/ui/UI.java	Sat Feb 09 21:52:38 2013 +0100
     1.2 +++ b/remoting/server/web/web.ui/src/org/netbeans/modules/jackpot30/backend/ui/UI.java	Sun Feb 10 14:51:55 2013 +0100
     1.3 @@ -199,16 +199,83 @@
     1.4      }
     1.5  
     1.6      @GET
     1.7 +    @Path("/searchSymbol")
     1.8 +    @Produces("application/javascript")
     1.9 +    public String searchSymbol(@Context UriInfo uriInfo, @QueryParam("path") List<String> paths, @QueryParam("prefix") String prefix) throws URISyntaxException, IOException, TemplateException {
    1.10 +        String urlBase = URL_BASE_OVERRIDE != null ? URL_BASE_OVERRIDE : uriInfo.getBaseUri().toString();
    1.11 +        Map<String, Map<?, ?>> segment2Result = new HashMap<String, Map<?, ?>>();
    1.12 +
    1.13 +        if (paths == null) {
    1.14 +            Map<String,Map<String, String>> pathsList = list(urlBase);
    1.15 +            paths = new ArrayList<String>(pathsList.keySet());
    1.16 +        }
    1.17 +
    1.18 +        for (String path : paths) {
    1.19 +            URI u = new URI(urlBase + "index/symbol/search?path=" + escapeForQuery(path) + "&prefix=" + escapeForQuery(prefix));
    1.20 +            @SuppressWarnings("unchecked") //XXX: should not trust something got from the network!
    1.21 +            Map<String, List<Map<String, Object>>> symbols = Pojson.load(LinkedHashMap.class, u);
    1.22 +
    1.23 +            URI typeSearch = new URI(urlBase + "index/type/search?path=" + escapeForQuery(path) + "&prefix=" + escapeForQuery(prefix));
    1.24 +            @SuppressWarnings("unchecked") //XXX: should not trust something got from the network!
    1.25 +            Map<String, List<String>> types = Pojson.load(LinkedHashMap.class, typeSearch);
    1.26 +
    1.27 +            for (Entry<String, List<String>> e : types.entrySet()) {
    1.28 +                List<Map<String, Object>> thisSourceRootResults = new ArrayList<Map<String, Object>>(e.getValue().size());
    1.29 +                for (String fqn : e.getValue()) {
    1.30 +                    Map<String, Object> result = new HashMap<String, Object>();
    1.31 +
    1.32 +                    result.put("kind", "CLASS");
    1.33 +                    result.put("fqn", fqn);
    1.34 +
    1.35 +                    String simpleName = fqn;
    1.36 +                    String enclosingFQN = "";
    1.37 +
    1.38 +                    if (simpleName.lastIndexOf('.') > 0) {
    1.39 +                        simpleName = simpleName.substring(simpleName.lastIndexOf('.') + 1);
    1.40 +                        enclosingFQN = fqn.substring(0, fqn.lastIndexOf('.'));
    1.41 +                    }
    1.42 +
    1.43 +                    if (simpleName.lastIndexOf('$') > 0) {
    1.44 +                        simpleName = simpleName.substring(simpleName.lastIndexOf('$') + 1);
    1.45 +                        enclosingFQN = fqn.substring(0, fqn.lastIndexOf('$'));
    1.46 +                    }
    1.47 +
    1.48 +                    result.put("simpleName", simpleName);
    1.49 +                    result.put("enclosingFQN", enclosingFQN);
    1.50 +
    1.51 +                    if (fqn.contains("$")) {
    1.52 +                        fqn = fqn.substring(0, fqn.indexOf("$"));
    1.53 +                    }
    1.54 +
    1.55 +                    result.put("file", fqn.replace('.', '/') + ".java");
    1.56 +
    1.57 +                    thisSourceRootResults.add(result);
    1.58 +                }
    1.59 +
    1.60 +                List<Map<String, Object>> putInto = symbols.get(e.getKey());
    1.61 +
    1.62 +                if (putInto == null) symbols.put(e.getKey(), thisSourceRootResults);
    1.63 +                else putInto.addAll(thisSourceRootResults);
    1.64 +            }
    1.65 +
    1.66 +            segment2Result.put(path, Collections.singletonMap("found", symbols));
    1.67 +        }
    1.68 +
    1.69 +        return Pojson.save(segment2Result);
    1.70 +    }
    1.71 +
    1.72 +    @GET
    1.73      @Path("/show")
    1.74      @Produces("text/html")
    1.75      public String show(@Context UriInfo uriInfo, @QueryParam("path") String segment, @QueryParam("relative") String relative, @QueryParam("highlight") String highlightSpec, @QueryParam("signature") String signature) throws URISyntaxException, IOException, TemplateException, InterruptedException {
    1.76 +        CategoryStorage category = CategoryStorage.forId(segment);
    1.77          String urlBase = URL_BASE_OVERRIDE != null ? URL_BASE_OVERRIDE : uriInfo.getBaseUri().toString();
    1.78          URI u = new URI(urlBase + "index/source/cat?path=" + escapeForQuery(segment) + "&relative=" + escapeForQuery(relative));
    1.79          String content = WebUtilities.requestStringResponse(u).replace("\r\n", "\n");
    1.80          List<Long> highlightSpans = new ArrayList<Long>();
    1.81 +        CompilationInfo info = ResolveService.parse(segment, relative);
    1.82  
    1.83          if (signature != null) {
    1.84 -            CompilationInfo info = ResolveService.parse(segment, relative);
    1.85              List<long[]> spans = ResolveService.usages(info, signature);
    1.86  
    1.87              for (long[] span : spans) {
    1.88 @@ -222,32 +289,39 @@
    1.89          }
    1.90  
    1.91          Map<String, Object> configurationData = new HashMap<String, Object>();
    1.92 -        String[] highlights = colorTokens(segment, relative, highlightSpans);
    1.93 +        HighlightData highlights = colorTokens(info, segment, relative, highlightSpans);
    1.94  
    1.95 -        configurationData.put("spans", highlights[0]);
    1.96 -        configurationData.put("categories", highlights[1]);
    1.97 +        configurationData.put("spans", toString(highlights.spans));
    1.98 +        configurationData.put("categories", toString(highlights.categories));
    1.99          configurationData.put("code", translate(content));
   1.100          configurationData.put("path", segment);
   1.101 +        configurationData.put("category", category.getDisplayName());
   1.102          configurationData.put("relative", relative);
   1.103  
   1.104          return FreemarkerUtilities.processTemplate("org/netbeans/modules/jackpot30/backend/ui/showCode.html", configurationData);
   1.105      }
   1.106  
   1.107 -    static String[] colorTokens(String segment, String relative, List<Long> highlight) throws IOException, InterruptedException {
   1.108 +    @GET
   1.109 +    @Path("/highlightData")
   1.110 +    @Produces("application/javascript")
   1.111 +    public String highlightData(@Context UriInfo uriInfo, @QueryParam("path") String segment, @QueryParam("relative") String relative) throws URISyntaxException, IOException, TemplateException, InterruptedException {
   1.112          CompilationInfo info = ResolveService.parse(segment, relative);
   1.113 +        HighlightData highlights = colorTokens(info, segment, relative, Collections.<Long>emptyList());
   1.114 +
   1.115 +        return Pojson.save(highlights);
   1.116 +    }
   1.117 +
   1.118 +    static HighlightData colorTokens(CompilationInfo info, String segment, String relative, List<Long> highlight) throws IOException, InterruptedException {
   1.119          TokenSequence<?> ts = info.getTokenHierarchy().tokenSequence(JavaTokenId.language());
   1.120          Map<Token, Coloring> semanticHighlights = SemanticHighlighter.computeHighlights(info, new TokenList(info, ts, new AtomicBoolean()));
   1.121 -        StringBuilder spans = new StringBuilder();
   1.122 -        StringBuilder cats  = new StringBuilder();
   1.123 +        List<Long> spans = new ArrayList<Long>(ts.tokenCount());
   1.124 +        List<String> cats  = new ArrayList<String>(ts.tokenCount());
   1.125          long currentOffset = 0;
   1.126          boolean cont = false;
   1.127  
   1.128          ts.moveStart();
   1.129  
   1.130          while (cont || ts.moveNext()) {
   1.131 -            if (spans.length() > 0) spans.append(", ");
   1.132 -            if (cats.length() > 0) cats.append(", ");
   1.133 -
   1.134              long endOffset = ts.offset() + ts.token().length();
   1.135              boolean foundHighlight = false;
   1.136  
   1.137 @@ -277,13 +351,17 @@
   1.138                  }
   1.139              }
   1.140  
   1.141 -            spans.append(endOffset - currentOffset);
   1.142 +            spans.add(endOffset - currentOffset);
   1.143              String category = ts.token().id().primaryCategory();
   1.144  
   1.145              if ("keyword-directive".equals(category)) {
   1.146                  category = "keyword";
   1.147              }
   1.148  
   1.149 +            if ("literal".equals(category)) {
   1.150 +                category = "keyword";
   1.151 +            }
   1.152 +
   1.153              if (foundHighlight) {
   1.154                  if (!category.isEmpty()) category += " ";
   1.155                  category += "highlight";
   1.156 @@ -298,15 +376,32 @@
   1.157                  }
   1.158              }
   1.159  
   1.160 -            cats.append(category);
   1.161 +            cats.add(category);
   1.162  
   1.163              currentOffset = endOffset;
   1.164          }
   1.165  
   1.166 -        return new String[] {
   1.167 -            spans.toString(),
   1.168 -            cats.toString()
   1.169 -        };
   1.170 +        return new HighlightData(cats, spans);
   1.171 +    }
   1.172 +
   1.173 +    private static String toString(List<?> list) {
   1.174 +        StringBuilder result = new StringBuilder();
   1.175 +
   1.176 +        for (Object o : list) {
   1.177 +            if (result.length() > 0) result.append(", ");
   1.178 +            result.append(String.valueOf(o));
   1.179 +        }
   1.180 +
   1.181 +        return result.toString();
   1.182 +    }
   1.183 +
   1.184 +    private static final class HighlightData {
   1.185 +        List<String> categories;
   1.186 +        List<Long> spans;
   1.187 +        public HighlightData(List<String> cats, List<Long> spans) {
   1.188 +            this.categories = cats;
   1.189 +            this.spans = spans;
   1.190 +        }
   1.191      }
   1.192  
   1.193      //XXX: usages on fields do not work because the field signature in the index contain also the field type
   1.194 @@ -434,6 +529,99 @@
   1.195          public Object compute(String segment) throws URISyntaxException, TemplateException, IOException;
   1.196      }
   1.197  
   1.198 +    @GET
   1.199 +    @Path("/searchUsages")
   1.200 +    @Produces("application/javascript")
   1.201 +    public String searchUsages(@Context UriInfo uriInfo, @QueryParam("path") List<String> paths, @QueryParam("signature") final String signature) throws URISyntaxException, IOException, TemplateException {
   1.202 +        String urlBase = URL_BASE_OVERRIDE != null ? URL_BASE_OVERRIDE : uriInfo.getBaseUri().toString();
   1.203 +        Map<String, Map<?, ?>> segment2Result = new HashMap<String, Map<?, ?>>();
   1.204 +
   1.205 +        if (paths == null) {
   1.206 +            Map<String,Map<String, String>> pathsList = list(urlBase);
   1.207 +            paths = new ArrayList<String>(pathsList.keySet());
   1.208 +        }
   1.209 +
   1.210 +        for (String path : paths) {
   1.211 +            Map<String, Map<String, Object>> usageFile2Flags = new HashMap<String, Map<String, Object>>();
   1.212 +            
   1.213 +            URI usagesURI = new URI(urlBase + "index/usages/search?path=" + escapeForQuery(path) + "&signatures=" + escapeForQuery(signature));
   1.214 +            List<String> files = new ArrayList<String>(WebUtilities.requestStringArrayResponse(usagesURI));
   1.215 +
   1.216 +            for (String file : files) {
   1.217 +                Map<String, Object> flags = usageFile2Flags.get(file);
   1.218 +
   1.219 +                if (flags == null) {
   1.220 +                    usageFile2Flags.put(file, flags = new HashMap<String, Object>());
   1.221 +                }
   1.222 +
   1.223 +                flags.put("usage", "true");
   1.224 +            }
   1.225 +
   1.226 +            if (signature.startsWith("CLASS:") || signature.startsWith("INTERFACE:") || signature.startsWith("ENUM:") || signature.startsWith("ANNOTATION_TYPE:")) {
   1.227 +                final String type = strip(signature, "CLASS:", "INTERFACE:", "ENUM:", "ANNOTATION_TYPE:");
   1.228 +                URI u = new URI(urlBase + "index/implements/search?path=" + escapeForQuery(path) + "&type=" + escapeForQuery(type));
   1.229 +                Map<String, List<Map<String, String>>> data = Pojson.load(HashMap.class, u);
   1.230 +                for (Entry<String, List<Map<String, String>>> relpath2ImplementorsE : data.entrySet()) {
   1.231 +                    for (Map<String, String> implementorData : relpath2ImplementorsE.getValue()) {
   1.232 +                        String file = implementorData.get("file");
   1.233 +                        Map<String, Object> flags = usageFile2Flags.get(file);
   1.234 +
   1.235 +                        if (flags == null) {
   1.236 +                            usageFile2Flags.put(file, flags = new HashMap<String, Object>());
   1.237 +                        }
   1.238 +
   1.239 +                        List<String> implementors = (List<String>) flags.get("subtypes");
   1.240 +
   1.241 +                        if (implementors == null) {
   1.242 +                            flags.put("subtypes", implementors = new ArrayList<String>());
   1.243 +                        }
   1.244 +
   1.245 +                        implementors.add(implementorData.get("class"));
   1.246 +                    }
   1.247 +                }
   1.248 +            } else if (signature.startsWith("METHOD:")) {
   1.249 +                URI u = new URI(urlBase + "index/implements/search?path=" + escapeForQuery(path) + "&method=" + escapeForQuery(signature));
   1.250 +                Map<String, List<Map<String, String>>> data = Pojson.load(HashMap.class, u);
   1.251 +                for (Entry<String, List<Map<String, String>>> relpath2ImplementorsE : data.entrySet()) {
   1.252 +                    for (Map<String, String> implementorData : relpath2ImplementorsE.getValue()) {
   1.253 +                        String file = implementorData.get("file");
   1.254 +                        Map<String, Object> flags = usageFile2Flags.get(file);
   1.255 +
   1.256 +                        if (flags == null) {
   1.257 +                            usageFile2Flags.put(file, flags = new HashMap<String, Object>());
   1.258 +                        }
   1.259 +
   1.260 +                        List<String> overridersParents = (List<String>) flags.get("overridersParents");
   1.261 +
   1.262 +                        if (overridersParents == null) {
   1.263 +                            flags.put("overridersParents", overridersParents = new ArrayList<String>());
   1.264 +                        }
   1.265 +
   1.266 +                        overridersParents.add(implementorData.get("enclosingFQN"));
   1.267 +                    }
   1.268 +                }
   1.269 +            }
   1.270 +            
   1.271 +            segment2Result.put(path, usageFile2Flags);
   1.272 +        }
   1.273 +
   1.274 +        return Pojson.save(segment2Result);
   1.275 +    }
   1.276 +
   1.277 +    @GET
   1.278 +    @Path("/localUsages")
   1.279 +    @Produces("application/javascript")
   1.280 +    public String localUsages(@Context UriInfo uriInfo, @QueryParam("path") String segment, @QueryParam("relative") String relative, @QueryParam("signature") final String signature, @QueryParam("usages") boolean usages) throws URISyntaxException, IOException, TemplateException, InterruptedException {
   1.281 +        final CompilationInfo info = ResolveService.parse(segment, relative);
   1.282 +        List<long[]> result = new ArrayList<long[]>();
   1.283 +        
   1.284 +        for (long[] span : ResolveService.usages(info, signature)) {
   1.285 +            result.add(new long[] {span[2], span[3]});
   1.286 +        }
   1.287 +
   1.288 +        return Pojson.save(result);
   1.289 +    }
   1.290 +
   1.291      private static String elementDisplayName(String signatures) {
   1.292          StringBuilder elementDisplayName = new StringBuilder();
   1.293          String[] splitSignature = signatures.split(":");
   1.294 @@ -731,6 +919,7 @@
   1.295                          break;
   1.296                  }
   1.297                  result.put("menu", menu);
   1.298 +                result.put("signature", signature[0]);
   1.299              }
   1.300          } else {
   1.301              if (targetPosition[0] != (-2)) {