Dropping the old version of the web-UI - the new one is used instead.
authorJan Lahoda <jlahoda@netbeans.org>
Sat, 06 Jul 2013 21:36:18 +0200
changeset 9740a968eab2727
parent 973 ecaacce31034
child 975 ead9715c276f
Dropping the old version of the web-UI - the new one is used instead.
remoting/server/web/web.ui.frontend/public_html/index/ui/script.js
remoting/server/web/web.ui/src/org/netbeans/modules/jackpot30/backend/ui/UI.java
remoting/server/web/web.ui/src/org/netbeans/modules/jackpot30/backend/ui/implementors.html
remoting/server/web/web.ui/src/org/netbeans/modules/jackpot30/backend/ui/showCode.html
remoting/server/web/web.ui/src/org/netbeans/modules/jackpot30/backend/ui/ui-findType.html
remoting/server/web/web.ui/src/org/netbeans/modules/jackpot30/backend/ui/usages.html
     1.1 --- a/remoting/server/web/web.ui.frontend/public_html/index/ui/script.js	Sat Jul 06 15:15:21 2013 +0200
     1.2 +++ b/remoting/server/web/web.ui.frontend/public_html/index/ui/script.js	Sat Jul 06 21:36:18 2013 +0200
     1.3 @@ -1,5 +1,4 @@
     1.4 -function DeclarationSearch($scope, $http, $routeParams) {
     1.5 -    $scope.prefix = "";
     1.6 +function DeclarationSearch($scope, $location, $http, $routeParams, $route) {
     1.7      $scope.performQuery = function() {
     1.8          $scope.$parent.loading = true;
     1.9          $http.get('/index/ui/searchSymbol?prefix=' + $scope.prefix).success(function(data) {
    1.10 @@ -28,7 +27,8 @@
    1.11      $scope.performQueryDelayed = _.debounce($scope.performQuery, 2000);
    1.12      $scope.getElementIcon = getElementIcon;
    1.13      $scope.acceptByKind = acceptByKind;
    1.14 -    $scope.prefix = $routeParams.prefix || "";
    1.15 +    var prefix = $route ? $route.current.params.prefix : null;
    1.16 +    $scope.prefix = prefix != null ? prefix : "";
    1.17      $scope.symbolSignature = symbolSignature;
    1.18  
    1.19      $scope.showSearch = false;
    1.20 @@ -38,7 +38,7 @@
    1.21      $scope.showMethods = true;
    1.22      $scope.showOthers = true;
    1.23      
    1.24 -    if (typeof $routeParams.prefix !== 'undefined') {
    1.25 +    if (prefix != null) {
    1.26          $scope.performQuery();
    1.27      }
    1.28  }
     2.1 --- a/remoting/server/web/web.ui/src/org/netbeans/modules/jackpot30/backend/ui/UI.java	Sat Jul 06 15:15:21 2013 +0200
     2.2 +++ b/remoting/server/web/web.ui/src/org/netbeans/modules/jackpot30/backend/ui/UI.java	Sat Jul 06 21:36:18 2013 +0200
     2.3 @@ -49,20 +49,15 @@
     2.4  import com.sun.source.tree.VariableTree;
     2.5  import com.sun.source.util.TreePath;
     2.6  import com.sun.source.util.TreePathScanner;
     2.7 -import com.sun.tools.javac.code.Flags;
     2.8 -import com.sun.tools.javac.tree.JCTree.JCMethodDecl;
     2.9 -import freemarker.template.TemplateException;
    2.10  import java.io.IOException;
    2.11  import java.net.URI;
    2.12  import java.net.URISyntaxException;
    2.13  import java.util.ArrayList;
    2.14  import java.util.Collection;
    2.15  import java.util.Collections;
    2.16 -import java.util.Comparator;
    2.17  import java.util.HashMap;
    2.18  import java.util.Iterator;
    2.19  import java.util.LinkedHashMap;
    2.20 -import java.util.LinkedList;
    2.21  import java.util.List;
    2.22  import java.util.Map;
    2.23  import java.util.Map.Entry;
    2.24 @@ -75,17 +70,16 @@
    2.25  import javax.ws.rs.Produces;
    2.26  import javax.ws.rs.QueryParam;
    2.27  import javax.ws.rs.core.Context;
    2.28 +import javax.ws.rs.core.Response;
    2.29  import javax.ws.rs.core.UriInfo;
    2.30  import org.codeviation.pojson.Pojson;
    2.31  import org.netbeans.api.java.lexer.JavaTokenId;
    2.32  import org.netbeans.api.java.source.ElementHandle;
    2.33 -import org.netbeans.api.java.source.matching.Matcher;
    2.34  import org.netbeans.api.lexer.Token;
    2.35  import org.netbeans.api.lexer.TokenHierarchy;
    2.36  import org.netbeans.api.lexer.TokenSequence;
    2.37  import org.netbeans.api.xml.lexer.XMLTokenId;
    2.38  import org.netbeans.modules.jackpot30.backend.base.CategoryStorage;
    2.39 -import org.netbeans.modules.jackpot30.backend.base.FreemarkerUtilities;
    2.40  import org.netbeans.modules.jackpot30.backend.base.WebUtilities;
    2.41  import static org.netbeans.modules.jackpot30.backend.base.WebUtilities.escapeForQuery;
    2.42  import org.netbeans.modules.jackpot30.backend.ui.highlighting.ColoringAttributes;
    2.43 @@ -108,103 +102,15 @@
    2.44  
    2.45      @GET
    2.46      @Path("/search")
    2.47 -    @Produces("text/html")
    2.48 -    public String searchType(@Context UriInfo uriInfo, @QueryParam("path") List<String> paths, @QueryParam("prefix") String prefix) throws URISyntaxException, IOException, TemplateException {
    2.49 -        String urlBase = URL_BASE_OVERRIDE != null ? URL_BASE_OVERRIDE : uriInfo.getBaseUri().toString();
    2.50 -        Map<String, Object> configurationData = new HashMap<String, Object>();
    2.51 -        Map<String,Map<String, String>> pathsList = list(urlBase);
    2.52 -
    2.53 -        configurationData.put("paths", pathsList.values());
    2.54 -        configurationData.put("selectedPath", paths);
    2.55 -        configurationData.put("prefix", prefix);
    2.56 -
    2.57 -        if (prefix != null && paths != null) {
    2.58 -            List<Map<String, Object>> results = new LinkedList<Map<String, Object>>();
    2.59 -
    2.60 -            for (String path : paths) {
    2.61 -                URI u = new URI(urlBase + "index/symbol/search?path=" + escapeForQuery(path) + "&prefix=" + escapeForQuery(prefix));
    2.62 -                @SuppressWarnings("unchecked") //XXX: should not trust something got from the network!
    2.63 -                Map<String, List<Map<String, Object>>> symbols = Pojson.load(LinkedHashMap.class, u);
    2.64 -                Map<String, ?> segmentDescription = pathsList.get(path);
    2.65 -
    2.66 -                for (Entry<String, List<Map<String, Object>>> e : symbols.entrySet()) {
    2.67 -                    for (Map<String, Object> found : e.getValue()) {
    2.68 -                        found.put("icon", getElementIcon((String) found.get("kind"), (Collection<String>) found.get("modifiers")));
    2.69 -                        if ("METHOD".equals(found.get("kind")) || "CONSTRUCTOR".equals(found.get("kind"))) {
    2.70 -                            found.put("displayName", found.get("simpleName") + decodeMethodSignature((String) found.get("signature")));
    2.71 -                        } else {
    2.72 -                            found.put("displayName", found.get("simpleName"));
    2.73 -                        }
    2.74 -
    2.75 -                        found.put("segment", segmentDescription);
    2.76 -
    2.77 -                        results.add(found);
    2.78 -                    }
    2.79 -                }
    2.80 -
    2.81 -                URI typeSearch = new URI(urlBase + "index/type/search?path=" + escapeForQuery(path) + "&prefix=" + escapeForQuery(prefix));
    2.82 -                @SuppressWarnings("unchecked") //XXX: should not trust something got from the network!
    2.83 -                Map<String, List<String>> types = Pojson.load(LinkedHashMap.class, typeSearch);
    2.84 -
    2.85 -                for (Entry<String, List<String>> e : types.entrySet()) {
    2.86 -                    for (String fqn : e.getValue()) {
    2.87 -                        Map<String, Object> result = new HashMap<String, Object>();
    2.88 -
    2.89 -                        result.put("icon", getElementIcon("CLASS", Collections.<String>emptyList()));
    2.90 -                        result.put("kind", "CLASS");
    2.91 -                        result.put("fqn", fqn);
    2.92 -
    2.93 -                        String displayName = fqn;
    2.94 -                        String enclosingFQN = "";
    2.95 -
    2.96 -                        if (displayName.lastIndexOf('.') > 0) {
    2.97 -                            displayName = displayName.substring(displayName.lastIndexOf('.') + 1);
    2.98 -                            enclosingFQN = fqn.substring(0, fqn.lastIndexOf('.'));
    2.99 -                        }
   2.100 -
   2.101 -                        if (displayName.lastIndexOf('$') > 0) {
   2.102 -                            displayName = displayName.substring(displayName.lastIndexOf('$') + 1);
   2.103 -                            enclosingFQN = fqn.substring(0, fqn.lastIndexOf('$'));
   2.104 -                        }
   2.105 -
   2.106 -                        result.put("displayName", displayName);
   2.107 -                        result.put("enclosingFQN", enclosingFQN);
   2.108 -
   2.109 -                        if (fqn.contains("$")) {
   2.110 -                            fqn = fqn.substring(0, fqn.indexOf("$"));
   2.111 -                        }
   2.112 -
   2.113 -                        result.put("file", e.getKey() + fqn.replace('.', '/') + ".java");
   2.114 -
   2.115 -                        result.put("segment", segmentDescription);
   2.116 -
   2.117 -                        results.add(result);
   2.118 -                    }
   2.119 -                }
   2.120 -
   2.121 -                Collections.sort(results, new Comparator<Map<String, Object>>() {
   2.122 -                    @Override public int compare(Map<String, Object> o1, Map<String, Object> o2) {
   2.123 -                        int r = ((String) o1.get("displayName")).compareTo((String) o2.get("displayName"));
   2.124 -
   2.125 -                        if (r == 0) {
   2.126 -                            r = ((String) o1.get("enclosingFQN")).compareTo((String) o2.get("enclosingFQN"));
   2.127 -                        }
   2.128 -                        return r;
   2.129 -                    }
   2.130 -                });
   2.131 -
   2.132 -            }
   2.133 -
   2.134 -            configurationData.put("results", results);
   2.135 -        }
   2.136 -
   2.137 -        return FreemarkerUtilities.processTemplate("org/netbeans/modules/jackpot30/backend/ui/ui-findType.html", configurationData);
   2.138 +    public Response searchType(@Context UriInfo uriInfo, @QueryParam("path") List<String> paths, @QueryParam("prefix") String prefix) throws URISyntaxException, IOException {
   2.139 +        URI base = uriInfo.getBaseUri();
   2.140 +        return Response.seeOther(new URI(base.getScheme(), base.getUserInfo(), base.getHost(), base.getPort(), base.getPath() + "index/ui/index.html", null, "/search?prefix=" + prefix)).build();
   2.141      }
   2.142  
   2.143      @GET
   2.144      @Path("/searchSymbol")
   2.145      @Produces("application/javascript")
   2.146 -    public String searchSymbol(@Context UriInfo uriInfo, @QueryParam("path") List<String> paths, @QueryParam("prefix") String prefix) throws URISyntaxException, IOException, TemplateException {
   2.147 +    public String searchSymbol(@Context UriInfo uriInfo, @QueryParam("path") List<String> paths, @QueryParam("prefix") String prefix) throws URISyntaxException, IOException {
   2.148          String urlBase = URL_BASE_OVERRIDE != null ? URL_BASE_OVERRIDE : uriInfo.getBaseUri().toString();
   2.149          Map<String, Map<?, ?>> segment2Result = new HashMap<String, Map<?, ?>>();
   2.150  
   2.151 @@ -269,63 +175,26 @@
   2.152  
   2.153      @GET
   2.154      @Path("/show")
   2.155 -    @Produces("text/html")
   2.156 -    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 {
   2.157 -        CategoryStorage category = CategoryStorage.forId(segment);
   2.158 -        String urlBase = URL_BASE_OVERRIDE != null ? URL_BASE_OVERRIDE : uriInfo.getBaseUri().toString();
   2.159 -        URI u = new URI(urlBase + "index/source/cat?path=" + escapeForQuery(segment) + "&relative=" + escapeForQuery(relative));
   2.160 -        String content = WebUtilities.requestStringResponse(u).replace("\r\n", "\n");
   2.161 -        HighlightData highlights;
   2.162 -
   2.163 -        if (relative.endsWith(".java")) {
   2.164 -            List<Long> highlightSpans = new ArrayList<Long>();
   2.165 -            CompilationInfo info = ResolveService.parse(segment, relative);
   2.166 -
   2.167 -            if (signature != null) {
   2.168 -                List<long[]> spans = ResolveService.usages(info, signature);
   2.169 -
   2.170 -                for (long[] span : spans) {
   2.171 -                    highlightSpans.add(span[0]);
   2.172 -                    highlightSpans.add(span[1]);
   2.173 -                }
   2.174 -            }
   2.175 -
   2.176 -            if (highlightSpec != null) {
   2.177 -                highlightSpans.addAll(Pojson.load(ArrayList.class, highlightSpec));
   2.178 -            }
   2.179 -
   2.180 -            highlights = colorTokens(info, highlightSpans);
   2.181 -        } else {
   2.182 -            highlights = new HighlightData(Collections.<String>emptyList(), Collections.<Long>emptyList());
   2.183 -        }
   2.184 -
   2.185 -        Map<String, Object> configurationData = new HashMap<String, Object>();
   2.186 -
   2.187 -        configurationData.put("spans", toString(highlights.spans));
   2.188 -        configurationData.put("categories", toString(highlights.categories));
   2.189 -        configurationData.put("code", translate(content));
   2.190 -        configurationData.put("path", segment);
   2.191 -        configurationData.put("category", category.getDisplayName());
   2.192 -        configurationData.put("relative", relative);
   2.193 -
   2.194 -        return FreemarkerUtilities.processTemplate("org/netbeans/modules/jackpot30/backend/ui/showCode.html", configurationData);
   2.195 +    public Response show(@Context UriInfo uriInfo, @QueryParam("path") String segment, @QueryParam("relative") String relative, @QueryParam("highlight") String highlightSpec, @QueryParam("signature") String signature) throws URISyntaxException, IOException, InterruptedException {
   2.196 +        URI base = uriInfo.getBaseUri();
   2.197 +        return Response.seeOther(new URI(base.getScheme(), base.getUserInfo(), base.getHost(), base.getPort(), base.getPath() + "index/ui/index.html", null, "/showCode?path=" + segment + "&relative=" + relative + "&goto=" + signature)).build();
   2.198      }
   2.199  
   2.200      @GET
   2.201      @Path("/highlightData")
   2.202      @Produces("application/javascript")
   2.203 -    public String highlightData(@Context UriInfo uriInfo, @QueryParam("path") String segment, @QueryParam("relative") String relative) throws URISyntaxException, IOException, TemplateException, InterruptedException {
   2.204 +    public String highlightData(@Context UriInfo uriInfo, @QueryParam("path") String segment, @QueryParam("relative") String relative) throws URISyntaxException, IOException, InterruptedException {
   2.205          HighlightData highlights;
   2.206          
   2.207          if (relative.endsWith(".java")) {
   2.208              CompilationInfo info = ResolveService.parse(segment, relative);
   2.209 -            highlights = colorTokens(info, Collections.<Long>emptyList());
   2.210 +            highlights = colorTokens(info);
   2.211          } else {
   2.212              String urlBase = URL_BASE_OVERRIDE != null ? URL_BASE_OVERRIDE : uriInfo.getBaseUri().toString();
   2.213              URI u = new URI(urlBase + "index/source/cat?path=" + escapeForQuery(segment) + "&relative=" + escapeForQuery(relative));
   2.214              String content = WebUtilities.requestStringResponse(u).replace("\r\n", "\n");
   2.215              if (relative.endsWith(".xml")) {
   2.216 -                highlights = colorTokens(TokenHierarchy.create(content, XMLTokenId.language()).tokenSequence(XMLTokenId.language()), Collections.<Token, Coloring>emptyMap(), Collections.<Long>emptyList());
   2.217 +                highlights = colorTokens(TokenHierarchy.create(content, XMLTokenId.language()).tokenSequence(XMLTokenId.language()), Collections.<Token, Coloring>emptyMap());
   2.218              } else {
   2.219                  highlights = new HighlightData(Collections.<String>singletonList(""), Collections.<Long>singletonList((long) content.length()));
   2.220              }
   2.221 @@ -335,51 +204,22 @@
   2.222          return Pojson.save(highlights);
   2.223      }
   2.224  
   2.225 -    static HighlightData colorTokens(CompilationInfo info, List<Long> highlight) throws IOException, InterruptedException {
   2.226 +    static HighlightData colorTokens(CompilationInfo info) throws IOException, InterruptedException {
   2.227          TokenSequence<?> ts = info.getTokenHierarchy().tokenSequence(JavaTokenId.language());
   2.228          Map<Token, Coloring> semanticHighlights = SemanticHighlighter.computeHighlights(info, new TokenList(info, ts, new AtomicBoolean()));
   2.229  
   2.230 -        return colorTokens(ts, semanticHighlights, highlight);
   2.231 +        return colorTokens(ts, semanticHighlights);
   2.232      }
   2.233  
   2.234 -    static HighlightData colorTokens(TokenSequence<?> ts, Map<Token, Coloring> semanticHighlights, List<Long> highlight) throws IOException, InterruptedException {
   2.235 +    static HighlightData colorTokens(TokenSequence<?> ts, Map<Token, Coloring> semanticHighlights) throws IOException, InterruptedException {
   2.236          List<Long> spans = new ArrayList<Long>(ts.tokenCount());
   2.237          List<String> cats  = new ArrayList<String>(ts.tokenCount());
   2.238          long currentOffset = 0;
   2.239 -        boolean cont = false;
   2.240  
   2.241          ts.moveStart();
   2.242  
   2.243 -        while (cont || ts.moveNext()) {
   2.244 +        while (ts.moveNext()) {
   2.245              long endOffset = ts.offset() + ts.token().length();
   2.246 -            boolean foundHighlight = false;
   2.247 -
   2.248 -            cont = false;
   2.249 -            
   2.250 -            for (int i = 0; i < highlight.size(); i += 2) {
   2.251 -                if (   currentOffset <= highlight.get(i)
   2.252 -                    && endOffset >= highlight.get(i)) {
   2.253 -                    if (currentOffset < highlight.get(i)) {
   2.254 -                        endOffset = highlight.get(i);
   2.255 -                        cont = true;
   2.256 -                    } else  if ((highlight.get(i + 1) + 1) < endOffset) {
   2.257 -                        endOffset = highlight.get(i + 1) + 1;
   2.258 -                        cont = true;
   2.259 -                        foundHighlight = true;
   2.260 -                    } else {
   2.261 -                        foundHighlight = true;
   2.262 -                    }
   2.263 -                } else if (   highlight.get(i) <= currentOffset
   2.264 -                           && (highlight.get(i + 1) + 1) > endOffset) {
   2.265 -                    foundHighlight = true;
   2.266 -                } else if (   currentOffset < (highlight.get(i + 1) + 1)
   2.267 -                           && (highlight.get(i + 1) + 1) < endOffset) {
   2.268 -                    endOffset = highlight.get(i + 1) + 1;
   2.269 -                    cont = true;
   2.270 -                    foundHighlight = true;
   2.271 -                }
   2.272 -            }
   2.273 -
   2.274              spans.add(endOffset - currentOffset);
   2.275              String category = ts.token().id().primaryCategory();
   2.276  
   2.277 @@ -395,11 +235,6 @@
   2.278                  case "xml-value": category = "markup-attribute-value"; break;
   2.279              }
   2.280              
   2.281 -            if (foundHighlight) {
   2.282 -                if (!category.isEmpty()) category += " ";
   2.283 -                category += "highlight";
   2.284 -            }
   2.285 -
   2.286              Coloring coloring = semanticHighlights.get(ts.token());
   2.287  
   2.288              if (coloring != null) {
   2.289 @@ -417,17 +252,6 @@
   2.290          return new HighlightData(cats, spans);
   2.291      }
   2.292  
   2.293 -    private static String toString(List<?> list) {
   2.294 -        StringBuilder result = new StringBuilder();
   2.295 -
   2.296 -        for (Object o : list) {
   2.297 -            if (result.length() > 0) result.append(", ");
   2.298 -            result.append(String.valueOf(o));
   2.299 -        }
   2.300 -
   2.301 -        return result.toString();
   2.302 -    }
   2.303 -
   2.304      private static final class HighlightData {
   2.305          List<String> categories;
   2.306          List<Long> spans;
   2.307 @@ -440,132 +264,22 @@
   2.308      //XXX: usages on fields do not work because the field signature in the index contain also the field type
   2.309      @GET
   2.310      @Path("/usages")
   2.311 -    @Produces("text/html")
   2.312 -    public String usages(@Context UriInfo uriInfo, @QueryParam("path") String segment, @QueryParam("signatures") final String signatures) throws URISyntaxException, IOException, TemplateException {
   2.313 -        final String urlBase = URL_BASE_OVERRIDE != null ? URL_BASE_OVERRIDE : uriInfo.getBaseUri().toString();
   2.314 -        Map<String, Object> configurationData = usagesSubclassesImpl(urlBase, segment, signatures, "files", new ComputeSegmentData() {
   2.315 -            @Override public Object compute(String currentSegment) throws URISyntaxException, TemplateException, IOException {
   2.316 -                URI u = new URI(urlBase + "index/usages/search?path=" + escapeForQuery(currentSegment) + "&signatures=" + escapeForQuery(signatures));
   2.317 -                List<String> files = new ArrayList<String>(WebUtilities.requestStringArrayResponse(u));
   2.318 -                Collections.sort(files);
   2.319 -                return files;
   2.320 -            }
   2.321 -        });
   2.322 -
   2.323 -        return FreemarkerUtilities.processTemplate("org/netbeans/modules/jackpot30/backend/ui/usages.html", configurationData);
   2.324 +    public Response usages(@Context UriInfo uriInfo, @QueryParam("path") String segment, @QueryParam("signatures") final String signatures) throws URISyntaxException, IOException {
   2.325 +        URI base = uriInfo.getBaseUri();
   2.326 +        return Response.seeOther(new URI(base.getScheme(), base.getUserInfo(), base.getHost(), base.getPort(), base.getPath() + "index/ui/index.html", null, "#/usages?signature=" + signatures)).build();
   2.327      }
   2.328  
   2.329      @GET
   2.330      @Path("/implements")
   2.331 -    @Produces("text/html")
   2.332 -    public String impl(@Context UriInfo uriInfo, @QueryParam("path") String segment, @QueryParam("type") String typeSignature, @QueryParam("method") final String methodSignature) throws URISyntaxException, IOException, TemplateException {
   2.333 -        final String urlBase = URL_BASE_OVERRIDE != null ? URL_BASE_OVERRIDE : uriInfo.getBaseUri().toString();
   2.334 -        Map<String, Object> configurationData;
   2.335 -
   2.336 -        if (typeSignature != null) {
   2.337 -            final String type = strip(typeSignature, "CLASS:", "INTERFACE:", "ENUM:", "ANNOTATION_TYPE:");
   2.338 -            configurationData = usagesSubclassesImpl(urlBase, segment, typeSignature, "implementors", new ComputeSegmentData() {
   2.339 -                @Override
   2.340 -                public Object compute(String currentSegment) throws URISyntaxException, TemplateException, IOException {
   2.341 -                    URI u = new URI(urlBase + "index/implements/search?path=" + escapeForQuery(currentSegment) + "&type=" + escapeForQuery(type));
   2.342 -                    Map<String, List<Map<String, String>>> data = Pojson.load(HashMap.class, u);
   2.343 -                    List<Map<String, String>> implementors = new ArrayList<Map<String, String>>();
   2.344 -                    for (Entry<String, List<Map<String, String>>> relpath2ImplementorsE : data.entrySet()) {
   2.345 -                        for (Map<String, String> implementorData : relpath2ImplementorsE.getValue()) {
   2.346 -                            Map<String, String> implementor = new HashMap<String, String>();
   2.347 -
   2.348 -                            implementor.put("file", implementorData.get("file"));
   2.349 -                            implementor.put("class", implementorData.get("class"));
   2.350 -                            implementors.add(implementor);
   2.351 -                        }
   2.352 -                    }
   2.353 -                    Collections.sort(implementors, new Comparator<Map<String, String>>() {
   2.354 -                        @Override
   2.355 -                        public int compare(Map<String, String> o1, Map<String, String> o2) {
   2.356 -                            return o1.get("class").compareTo(o2.get("class"));
   2.357 -                        }
   2.358 -                    });
   2.359 -                    return implementors;
   2.360 -                }
   2.361 -            });
   2.362 -
   2.363 -            configurationData.put("isSubtypes", true);
   2.364 -        } else {
   2.365 -            configurationData = usagesSubclassesImpl(urlBase, segment, methodSignature, "implementors", new ComputeSegmentData() {
   2.366 -                @Override
   2.367 -                public Object compute(String currentSegment) throws URISyntaxException, TemplateException, IOException {
   2.368 -                    URI u = new URI(urlBase + "index/implements/search?path=" + escapeForQuery(currentSegment) + "&method=" + escapeForQuery(methodSignature));
   2.369 -                    Map<String, List<Map<String, String>>> data = Pojson.load(HashMap.class, u);
   2.370 -                    List<Map<String, String>> implementors = new ArrayList<Map<String, String>>();
   2.371 -                    for (Entry<String, List<Map<String, String>>> relpath2ImplementorsE : data.entrySet()) {
   2.372 -                        for (Map<String, String> implementorData : relpath2ImplementorsE.getValue()) {
   2.373 -                            Map<String, String> implementor = new HashMap<String, String>();
   2.374 -
   2.375 -                            implementor.put("file", implementorData.get("file"));
   2.376 -                            implementor.put("class", implementorData.get("enclosingFQN"));
   2.377 -                            implementors.add(implementor);
   2.378 -                        }
   2.379 -                    }
   2.380 -                    Collections.sort(implementors, new Comparator<Map<String, String>>() {
   2.381 -                        @Override
   2.382 -                        public int compare(Map<String, String> o1, Map<String, String> o2) {
   2.383 -                            return o1.get("class").compareTo(o2.get("class"));
   2.384 -                        }
   2.385 -                    });
   2.386 -                    return implementors;
   2.387 -                }
   2.388 -            });
   2.389 -
   2.390 -            configurationData.put("isSubtypes", false);
   2.391 -        }
   2.392 -
   2.393 -        return FreemarkerUtilities.processTemplate("org/netbeans/modules/jackpot30/backend/ui/implementors.html", configurationData);
   2.394 -    }
   2.395 -
   2.396 -    private Map<String, Object> usagesSubclassesImpl(String urlBase, String segment, String elementSignature, String dataKey, ComputeSegmentData computeSegmentData) throws URISyntaxException, TemplateException, IOException {
   2.397 -        List<Map<String, String>> segments2Process = new ArrayList<Map<String, String>>();
   2.398 -
   2.399 -        for (Map<String, String> m : list(urlBase).values()) {
   2.400 -            if (segment != null) {
   2.401 -                if (segment.equals(m.get("segment"))) {
   2.402 -                    segments2Process.add(m);
   2.403 -                }
   2.404 -            } else {
   2.405 -                segments2Process.add(m);
   2.406 -            }
   2.407 -        }
   2.408 -
   2.409 -        Map<String, Object> configurationData = new HashMap<String, Object>();
   2.410 -
   2.411 -        configurationData.put("elementDisplayName", elementDisplayName(elementSignature)); //TODO
   2.412 -        configurationData.put("elementSignature", elementSignature); //TODO
   2.413 -
   2.414 -        List<Map<String, Object>> results = new ArrayList<Map<String, Object>>(segments2Process.size());
   2.415 -
   2.416 -        for (Map<String, String> m : segments2Process) {
   2.417 -            Map<String, Object> rootResults = new HashMap<String, Object>();
   2.418 -            String currentSegment = m.get("segment");
   2.419 -
   2.420 -            rootResults.put("rootDisplayName", m.get("displayName"));
   2.421 -            rootResults.put("rootPath", currentSegment);
   2.422 -            rootResults.put(dataKey, computeSegmentData.compute(currentSegment));
   2.423 -
   2.424 -            results.add(rootResults);
   2.425 -        }
   2.426 -
   2.427 -        configurationData.put("results", results);
   2.428 -
   2.429 -        return configurationData;
   2.430 -    }
   2.431 -
   2.432 -    private interface ComputeSegmentData {
   2.433 -        public Object compute(String segment) throws URISyntaxException, TemplateException, IOException;
   2.434 +    public Response impl(@Context UriInfo uriInfo, @QueryParam("path") String segment, @QueryParam("type") String typeSignature, @QueryParam("method") final String methodSignature) throws URISyntaxException, IOException {
   2.435 +        URI base = uriInfo.getBaseUri();
   2.436 +        return Response.seeOther(new URI(base.getScheme(), base.getUserInfo(), base.getHost(), base.getPort(), base.getPath() + "index/ui/index.html", null, "#/usages?signature=" + (typeSignature != null ? typeSignature : methodSignature))).build();
   2.437      }
   2.438  
   2.439      @GET
   2.440      @Path("/searchUsages")
   2.441      @Produces("application/javascript")
   2.442 -    public String searchUsages(@Context UriInfo uriInfo, @QueryParam("path") List<String> paths, @QueryParam("signature") final String signature) throws URISyntaxException, IOException, TemplateException {
   2.443 +    public String searchUsages(@Context UriInfo uriInfo, @QueryParam("path") List<String> paths, @QueryParam("signature") final String signature) throws URISyntaxException, IOException {
   2.444          String urlBase = URL_BASE_OVERRIDE != null ? URL_BASE_OVERRIDE : uriInfo.getBaseUri().toString();
   2.445          Map<String, Map<?, ?>> segment2Result = new HashMap<String, Map<?, ?>>();
   2.446  
   2.447 @@ -644,7 +358,7 @@
   2.448      @GET
   2.449      @Path("/localUsages")
   2.450      @Produces("application/javascript")
   2.451 -    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 {
   2.452 +    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, InterruptedException {
   2.453          List<long[]> result = new ArrayList<long[]>();
   2.454  
   2.455          if (relative.endsWith(".java")) {
   2.456 @@ -898,7 +612,7 @@
   2.457      @GET
   2.458      @Path("/target")
   2.459  //    @Produces("text/html")
   2.460 -    public String target(@QueryParam("path") String segment, @QueryParam("relative") String relative, @QueryParam("position") final long position) throws URISyntaxException, IOException, TemplateException, InterruptedException {
   2.461 +    public String target(@QueryParam("path") String segment, @QueryParam("relative") String relative, @QueryParam("position") final long position) throws URISyntaxException, IOException, InterruptedException {
   2.462          final CompilationInfo info = ResolveService.parse(segment, relative);
   2.463          final boolean[] declaration = new boolean[1];
   2.464          final long[] targetPosition = new long[] { -2 };
   2.465 @@ -1051,7 +765,7 @@
   2.466      @GET
   2.467      @Path("/declarationSpan")
   2.468  //    @Produces("text/html")
   2.469 -    public String declarationSpan(@QueryParam("path") String segment, @QueryParam("relative") String relative, @QueryParam("signature") String signature) throws URISyntaxException, IOException, TemplateException, InterruptedException {
   2.470 +    public String declarationSpan(@QueryParam("path") String segment, @QueryParam("relative") String relative, @QueryParam("signature") String signature) throws URISyntaxException, IOException, InterruptedException {
   2.471          CompilationInfo info = ResolveService.parse(segment, relative);
   2.472          long[] span = ResolveService.declarationSpans(info, signature);
   2.473  
     3.1 --- a/remoting/server/web/web.ui/src/org/netbeans/modules/jackpot30/backend/ui/implementors.html	Sat Jul 06 15:15:21 2013 +0200
     3.2 +++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
     3.3 @@ -1,24 +0,0 @@
     3.4 -<html>
     3.5 -<head>
     3.6 -    <script type="text/javascript" src="https://ajax.googleapis.com/ajax/libs/jquery/1.7.1/jquery.min.js"></script>
     3.7 -    <script type="text/javascript" src="https://ajax.googleapis.com/ajax/libs/jqueryui/1.8.18/jquery-ui.min.js"></script>
     3.8 -    <link href="http://ajax.googleapis.com/ajax/libs/jqueryui/1.8.18/themes/base/jquery-ui.css" rel="stylesheet" type="text/css">
     3.9 -</head>
    3.10 -<body>
    3.11 -    <#if isSubtypes>
    3.12 -        Subtypes of ${elementDisplayName?replace("&", "&amp;")?replace("<", "&lt;")}:<br>
    3.13 -    <#else>
    3.14 -        Overriders of ${elementDisplayName?replace("&", "&amp;")?replace("<", "&lt;")} found in the following classes:<br>
    3.15 -    </#if>
    3.16 -        <ul>
    3.17 -            <#list results as rootResults>
    3.18 -                <li>${rootResults.rootDisplayName?replace("&", "&amp;")?replace("<", "&lt;")}<br>
    3.19 -                    <#list rootResults.implementors as implementor>
    3.20 -                        <img src="/index/icons/class.png" alt="Java Class"/>
    3.21 -                        <a href='/index/ui/show?path=${rootResults.rootPath}&relative=${implementor.file}'>${implementor.class}</a><br>
    3.22 -                    </#list>
    3.23 -                </li>
    3.24 -            </#list>
    3.25 -        </ul>
    3.26 -</body>
    3.27 -</html>
     4.1 --- a/remoting/server/web/web.ui/src/org/netbeans/modules/jackpot30/backend/ui/showCode.html	Sat Jul 06 15:15:21 2013 +0200
     4.2 +++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
     4.3 @@ -1,113 +0,0 @@
     4.4 -<html>
     4.5 -<head>
     4.6 -    <script type="text/javascript" src="https://ajax.googleapis.com/ajax/libs/jquery/1.7.1/jquery.min.js"></script>
     4.7 -    <script type="text/javascript" src="https://ajax.googleapis.com/ajax/libs/jqueryui/1.8.18/jquery-ui.min.js"></script>
     4.8 -    <link href="http://ajax.googleapis.com/ajax/libs/jqueryui/1.8.18/themes/base/jquery-ui.css" rel="stylesheet" type="text/css">
     4.9 -    <script type="text/javascript">
    4.10 -        $(document).ready(function() {
    4.11 -            var $codeEl = $("#code");
    4.12 -            var $code = $codeEl.text();
    4.13 -
    4.14 -            $codeEl.empty();
    4.15 -            
    4.16 -            var $highlights = "${categories}".split(", ");
    4.17 -            var $spans = [${spans}];
    4.18 -            var $current = 0;
    4.19 -            for (var i = 0; i < $highlights.length; i++ ) {
    4.20 -                $codeEl.append($('<a id="p' + $current + '"><span class="' + $highlights[i] + '"' + ($highlights[i].indexOf("identifier") !== (-1) ? ' onclick="ic(' + $current + ')" jpt30pos="' + $current + '"' : '') + '>' + $code.slice($current, $current+$spans[i]).replace(/&/g, '&amp;').replace(/</g, '&lt;') + "</span></a>"));
    4.21 -                $current += $spans[i];
    4.22 -            }
    4.23 -
    4.24 -            if (!window.location.hash) {
    4.25 -                var $params = window.location.search.substring(1).split("&");
    4.26 -
    4.27 -                for (var i = 0; i < $params.length; i++) {
    4.28 -                    if ($params[i].indexOf("signature=") !== (-1)) {
    4.29 -                        $.get('/index/ui/declarationSpan?path=${path}&relative=${relative}&signature=' + unescape($params[i].substring("signature=".length)), function(data) {
    4.30 -                            var parsedData = $.parseJSON(data);
    4.31 -                            if (parsedData[2] !== (-1)) {
    4.32 -                                window.location.hash = "#p" + parsedData[2];
    4.33 -                            }
    4.34 -
    4.35 -                        });
    4.36 -                    }
    4.37 -                }
    4.38 -            } else {
    4.39 -                window.location.hash = window.location.hash;
    4.40 -            }
    4.41 -        });
    4.42 -
    4.43 -        function ic(pos) {
    4.44 -            $.get('/index/ui/target?path=${path}&relative=${relative}&position=' + pos, function(data) {
    4.45 -                var parsedData = $.parseJSON(data);
    4.46 -                if ("position" in parsedData) {
    4.47 -                    window.location.hash = "#p" + parsedData.position;
    4.48 -                } else if ("source" in parsedData) {
    4.49 -                    window.location = "/index/ui/show?path=" + parsedData.path + "&relative=" + parsedData.source + "&signature=" + parsedData.signature;
    4.50 -                } else if ("targets" in parsedData) {
    4.51 -                    var popupContent = "The target element is defined in the following files:<br>";
    4.52 -                    popupContent += "<ul>";
    4.53 -
    4.54 -                    for (var i = 0; i < parsedData.targets.length; i++) {
    4.55 -                        var categoryData = parsedData.targets[i];
    4.56 -                        popupContent += "<li>" + categoryData.rootDisplayName/*XXX: escape*/ + "<br>";
    4.57 -
    4.58 -                        for (var f = 0; f < categoryData.files.length; f++) {
    4.59 -                            popupContent += "<img src='/index/icons/javaFile.png' alt='Java File'/>"
    4.60 -                            popupContent += "<a href='/index/ui/show?path=" + categoryData.rootPath + "&relative=" + categoryData.files[i] + "&signature=" + parsedData.signature + "'>" + categoryData.files[i] + "</a><br>";
    4.61 -                        }
    4.62 -
    4.63 -                        popupContent += "</li><br>";
    4.64 -                    }
    4.65 -
    4.66 -                    popupContent += "</ul><br>";
    4.67 -                    $('#popup').html(popupContent)
    4.68 -                                    .dialog({
    4.69 -                                        title: 'Show',
    4.70 -                                        width: 800 //XXX: hardcoded size
    4.71 -                                    });
    4.72 -                } else if ("menu" in parsedData) {
    4.73 -                    var menuDef = parsedData.menu;
    4.74 -                    var popupContent = "";
    4.75 -                    for (var i = 0; i < menuDef.length; i++) {
    4.76 -                        var menuItem = menuDef[i];
    4.77 -                        popupContent += '<a href="' + menuItem.url + '">' + menuItem.displayName + '</a><br>';
    4.78 -                        $('#popup').html(popupContent)
    4.79 -                                        .dialog({
    4.80 -                                            title: 'Show',
    4.81 -                                            width: 800 //XXX: hardcoded size
    4.82 -                                        });
    4.83 -                    }
    4.84 -                } else if ("signature" in parsedData) {
    4.85 -                    alert("Cannot find source file for class: " + parsedData.signature.split(":")[1]);
    4.86 -                } else {
    4.87 -                    alert("Cannot resolve target on this place");
    4.88 -                }
    4.89 -            });
    4.90 -        }
    4.91 -    </script>
    4.92 -    <style type="text/css">
    4.93 -        .keyword {color: #0000FF; font-weight: bold;}
    4.94 -        .comment {color: #737373;}
    4.95 -        .character {color: #006F00;}
    4.96 -        .number {color: #780000;}
    4.97 -        .string {color: #99009D;}
    4.98 -        .identifier {}
    4.99 -        .identifier:hover { cursor: hand; cursor: pointer; text-decoration:underline; color:blue;}
   4.100 -        .whitespace {}
   4.101 -        .highlight {background-color:#DDDD00;}
   4.102 -        .constructor {font-weight: bold;}
   4.103 -        .method {font-weight: bold;}
   4.104 -        .field {color: #098618; font-weight: bold;}
   4.105 -        .parameter {color: #a06001;}
   4.106 -        .unused {color: gray;}
   4.107 -        .static {font-style:italic;}
   4.108 -    </style>
   4.109 -</head>
   4.110 -<body>
   4.111 -<pre id="code">
   4.112 -${code}
   4.113 -</pre>
   4.114 -<div id="popup"></div>
   4.115 -</body>
   4.116 -</html>
     5.1 --- a/remoting/server/web/web.ui/src/org/netbeans/modules/jackpot30/backend/ui/ui-findType.html	Sat Jul 06 15:15:21 2013 +0200
     5.2 +++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
     5.3 @@ -1,94 +0,0 @@
     5.4 -<html>
     5.5 -<head>
     5.6 -    <script type="text/javascript" src="https://ajax.googleapis.com/ajax/libs/jquery/1.7.1/jquery.min.js"></script>
     5.7 -    <script type="text/javascript" src="https://ajax.googleapis.com/ajax/libs/jqueryui/1.8.18/jquery-ui.min.js"></script>
     5.8 -    <link href="http://ajax.googleapis.com/ajax/libs/jqueryui/1.8.18/themes/base/jquery-ui.css" rel="stylesheet" type="text/css">
     5.9 -    <script type="text/javascript">
    5.10 -        function methodPopup(relativePath, resultFile, signature) {
    5.11 -            $('#popup').html('<a href="/index/ui/show?path=' + relativePath + '&relative=' + resultFile + '&signatures=' + signature + '">Source Code</a><br>' +
    5.12 -                             '<a href="/index/ui/usages?path=' + relativePath + '&signatures=' + signature + '">Usages in the defining project</a><br>' +
    5.13 -                             '<a href="/index/ui/usages?signatures=' + signature + '">Usages in all known projects</a><br>' +
    5.14 -                             '<a href="/index/ui/implements?path=' + relativePath + '&method=' + signature + '">Overriders in the defining project</a><br>' +
    5.15 -                             '<a href="/index/ui/implements?method=' + signature + '">Overriders in all known projects</a><br>')
    5.16 -                            .dialog({
    5.17 -                                title: 'Show',
    5.18 -                                width: 460 //XXX: hardcoded size
    5.19 -                            });
    5.20 -        }
    5.21 -        function classPopup(relativePath, resultFile, signature) {
    5.22 -            $('#popup').html('<a href="/index/ui/show?path=' + relativePath + '&relative=' + resultFile + '&signatures=' + signature + '">Source Code</a><br>' +
    5.23 -                             '<a href="/index/ui/usages?path=' + relativePath + '&signatures=' + signature + '">Usages in the defining project</a><br>' +
    5.24 -                             '<a href="/index/ui/usages?signatures=' + signature + '">Usages in all known projects</a><br>' +
    5.25 -                             '<a href="/index/ui/implements?path=' + relativePath + '&type=' + signature + '">Subtypes in the defining project</a><br>' +
    5.26 -                             '<a href="/index/ui/implements?type=' + signature + '">Subtypes in all known projects</a><br>')
    5.27 -                            .dialog({
    5.28 -                                title: 'Show',
    5.29 -                                width: 400 //XXX: hardcoded size
    5.30 -                            });
    5.31 -        }
    5.32 -        function otherPopup(relativePath, resultFile, signature) {
    5.33 -            $('#popup').html('<a href="/index/ui/show?path=' + relativePath + '&relative=' + resultFile + '&signatures=' + signature + '">Source Code</a><br>' +
    5.34 -                             '<a href="/index/ui/usages?path=' + relativePath + '&signatures=' + signature + '">Usages in the defining project</a><br>' +
    5.35 -                             '<a href="/index/ui/usages?signatures=' + signature + '">Usages in all known projects</a><br>')
    5.36 -                            .dialog({
    5.37 -                                title: 'Show'
    5.38 -                            });
    5.39 -        }
    5.40 -        function checkAllProjectsCheckBox() {
    5.41 -            allChecked = $('.projectCheckBox').filter(function(index) {
    5.42 -                return !this.checked;
    5.43 -            }).length == 0;
    5.44 -            if (!allChecked) $('#allProjectsCheckBox')[0].checked = false;
    5.45 -        }
    5.46 -        function checkCheckBoxes() {
    5.47 -            if (!$('#allProjectsCheckBox')[0].checked) return;
    5.48 -            allChecked = $('.projectCheckBox').each(function(index) {
    5.49 -                this.checked = true;
    5.50 -            }).length == 0;
    5.51 -        }
    5.52 -        $(document).ready(function() {
    5.53 -            checkAllProjectsCheckBox();
    5.54 -        })
    5.55 -    </script>
    5.56 -</head>
    5.57 -<body>
    5.58 -<form method="get">
    5.59 -<label for="prefix">Search for:</label><input type="text" name="prefix"<#if prefix??>value="${prefix}"</#if>/><br>
    5.60 -<label>Projects:</label>
    5.61 -<table><tr>
    5.62 -        <td>    <input id="allProjectsCheckBox" type="checkbox" onclick="checkCheckBoxes();" checked="yes">All</input>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</td>
    5.63 -<#list paths as path>
    5.64 -<td><input class="projectCheckBox" type="checkbox" name="path" onclick="checkAllProjectsCheckBox();" value="${path.segment}" <#if !selectedPath?? || selectedPath?seq_contains(path.segment)> checked="yes"</#if>>${path.displayName}</input>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</td>
    5.65 -<#if path_index % 5 == 3></tr><tr></#if>
    5.66 -</#list>
    5.67 -    </tr></table>
    5.68 -<br>
    5.69 -<input type="submit" name="Find Candidates"/>
    5.70 -</form>
    5.71 -
    5.72 -<#if results??>
    5.73 -    Found symbols:<br>
    5.74 -    <table>
    5.75 -        <#list results as result>
    5.76 -        <tr>
    5.77 -            <td>
    5.78 -                <nobr>
    5.79 -                <img src="/index/icons/${result.icon}" alt="${result.kind}"/>
    5.80 -                <#if result.kind == "METHOD">
    5.81 -                    <a href="javascript: methodPopup('${result.segment.segment}', '${result.file}', '${result.kind}:${result.enclosingFQN}:${result.simpleName}:${result.vmsignature}')">
    5.82 -                <#elseif result.kind == "CLASS" || result.kind == "INTERFACE" || result.kind == "ENUM" || result.kind == "ANNOTATION_TYPE">
    5.83 -                    <a href="javascript: classPopup('${result.segment.segment}', '${result.file}', '${result.kind}:${result.fqn}')">
    5.84 -                <#else>
    5.85 -                    <a href="javascript: otherPopup('${result.segment.segment}', '${result.file}', '${result.kind}:${result.enclosingFQN}:${result.simpleName}')">
    5.86 -                </#if>
    5.87 -                ${result.displayName?replace("&", "&amp;")?replace("<", "&lt;")}</a> in ${result.enclosingFQN?replace("&", "&amp;")?replace("<", "&lt;")}
    5.88 -                </nobr>
    5.89 -            </td>
    5.90 -            <td width="100%" align="right">${result.segment.displayName?replace("&", "&amp;")?replace("<", "&lt;")?replace(" ", "&nbsp;")}</td>
    5.91 -        </tr>
    5.92 -        </#list>
    5.93 -</#if>
    5.94 -
    5.95 -<div id="popup"></div>
    5.96 -</body>
    5.97 -</html>
     6.1 --- a/remoting/server/web/web.ui/src/org/netbeans/modules/jackpot30/backend/ui/usages.html	Sat Jul 06 15:15:21 2013 +0200
     6.2 +++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
     6.3 @@ -1,20 +0,0 @@
     6.4 -<html>
     6.5 -<head>
     6.6 -    <script type="text/javascript" src="https://ajax.googleapis.com/ajax/libs/jquery/1.7.1/jquery.min.js"></script>
     6.7 -    <script type="text/javascript" src="https://ajax.googleapis.com/ajax/libs/jqueryui/1.8.18/jquery-ui.min.js"></script>
     6.8 -    <link href="http://ajax.googleapis.com/ajax/libs/jqueryui/1.8.18/themes/base/jquery-ui.css" rel="stylesheet" type="text/css">
     6.9 -</head>
    6.10 -<body>
    6.11 -    Occurrences of ${elementDisplayName?replace("&", "&amp;")?replace("<", "&lt;")} found in the following files:<br>
    6.12 -        <ul>
    6.13 -            <#list results as rootResults>
    6.14 -                <li>${rootResults.rootDisplayName?replace("&", "&amp;")?replace("<", "&lt;")}<br>
    6.15 -                    <#list rootResults.files as file>
    6.16 -                        <img src="/index/icons/javaFile.png" alt="Java File"/>
    6.17 -                        <a href='/index/ui/show?path=${rootResults.rootPath}&relative=${file}&signature=${elementSignature}'>${file}</a><br>
    6.18 -                    </#list>
    6.19 -                </li>
    6.20 -            </#list>
    6.21 -        </ul>
    6.22 -</body>
    6.23 -</html>