Commits

J. Ryan Stinnett committed 3c639ab

Added UI to control search paging

Comments (0)

Files changed (4)

bazaarvoice-customizations

 # HG changeset patch
 # User J. Ryan Stinnett <jryans@gmail.com>
-# Date 1295344652 21600
+# Date 1295358064 21600
 # Node ID a0baf2a260b3a7d8c496cd32e7b323a571462f52
-# Parent 4dcd76ff7b5a03f5c7e5ceb42a0ab3e60d6e6197
+# Parent 2aea1e860afb6948117eae07481956669df9aebd
 Bazaarvoice-specific customizations
 
 diff --git a/src/org/opensolaris/opengrok/search/Results.java b/src/org/opensolaris/opengrok/search/Results.java
 diff --git a/web/default/style.css b/web/default/style.css
 --- a/web/default/style.css
 +++ b/web/default/style.css
-@@ -1,144 +1,199 @@
+@@ -1,144 +1,202 @@
 -body {
 -background-color: #ffffff;
 -/*font-size: 100.01%; /* 12px [1] */
 +            #headerMenu a:link, #headerMenu a:visited, #headerMenu a:hover, #headerMenu a:active {color: #ffffff;}
 +            #headerMenu form {margin-left: 1em;}
 +            .page-index #headerMenu form, .page-help #headerMenu form, .page-results #headerMenu form {display: none;}
-+                #headerMenu input[type=text] {font-size: 1.4em; width: 200px;}
++                #headerMenu input[type=text] {font-size: 1.4em;}
++                #headerMenu #search {width: 200px;}
++                #headerMenu .pageSize input {width: 40px; margin-top: 0.5em;}
++                #headerMenu .pageSize label {margin-left: 0.25em;}
 +                #headerMenu button {font-size: 1.2em; margin-left: 0.5em;}
 +                #headerMenu label {color: #666666; display: inline; font-size: 1.2em; text-shadow: none;}
 +                    #headerMenu label .code {color: #eeeeee;}
  
  .sl {color:#999;text-decoration:underline;}
  td.a{color:#000}
-@@ -146,7 +201,6 @@
+@@ -146,7 +204,6 @@
  .s  {color:green;} /* strings */
  .c  {color:#666;} /* comments */
  .b  {color:#000; font-weight:bold;} /* comments */
  .i  {color:#00008b; font-weight:bold;} /* interpolations */
  .di {color:#00008b; font-weight:bold;} /* directives */
  .bi {color:#666;} /* built-in */
-@@ -155,101 +209,33 @@
+@@ -155,101 +212,33 @@
  div.a {color:#000}
  a.h {font-size:small; margin-left:2em;}
  
  //List<org.apache.lucene.document.Document> docs=new ArrayList<org.apache.lucene.document.Document>();
  String errorMsg = null;
  
-@@ -99,23 +108,23 @@
- if (project != null && project.size()<1) project = null;
- 
- if (q != null || defs != null || refs != null || hist != null || path != null) {
--    Searcher searcher = null;		    //the searcher used to open/search the index
-+    Searcher searcher = null;            //the searcher used to open/search the index
-     TopScoreDocCollector collector=null;         // the collector used
-     ScoreDoc[] hits = null;                 // list of documents which result from the query
-     Query query = null;         //the Query created by the QueryBuilder
-     boolean allCollected=false;
-     int totalHits=0;
-     
--    int start = 0;		       //the first index displayed on this page
-+    int start = 0;               //the first index displayed on this page
-     //TODO deprecate max this and merge with paging and param n - TEST needed
--    //int max    = 25;			//the maximum items displayed on this page
-+    //int max    = 25;            //the maximum items displayed on this page
-     int max=RuntimeEnvironment.getInstance().getHitsPerPage();
- 
-     int hitsPerPage = RuntimeEnvironment.getInstance().getHitsPerPage();
-     int cachePages= RuntimeEnvironment.getInstance().getCachePages();
-     final boolean docsScoredInOrder=false;
- 
--    int thispage = 0;			    //used for the for/next either max or
-+    int thispage = 0;                //used for the for/next either max or
- 
-     QueryBuilder queryBuilder =
-             new QueryBuilder()
-@@ -125,16 +134,16 @@
+@@ -133,11 +142,11 @@
      try {
          String DATA_ROOT = env.getDataRootPath();
          if(DATA_ROOT.equals("")) {
 -            throw new Exception("DATA_ROOT parameter in web.xml does not exist or is not a directory!");
 +            throw new Exception("DATA_ROOT parameter in web.xml does not exist or is not a directory.");
          }
-         //String date = request.getParameter("date");
-         try {
-             //TODO merge paging hitsPerPage with parameter n (has to reflect the search if changed so proper number is cached first time)
--            start = Integer.parseInt(request.getParameter("start"));	//parse the max results first
-+            start = Integer.parseInt(request.getParameter("start"));    //parse the max results first
-             max = Integer.parseInt(request.getParameter("n"));      //then the start index
-             if(max < 0 || (max % 10 != 0) || max > 50) max = 25;
-             if(start < 0 ) start = 0;
-@@ -147,7 +156,7 @@
+ 
+         //TODO merge paging hitsPerPage with parameter n (has to reflect the search if changed so proper number is cached first time)
+@@ -166,7 +175,7 @@
  
          if (RuntimeEnvironment.getInstance().hasProjects()) {
              if (project == null) {
              } else {
                  if (project.size() > 1) { //more projects
                      IndexSearcher[] searchables = new IndexSearcher[project.size()];
-@@ -216,18 +225,18 @@
+@@ -220,18 +229,18 @@
+             }
          }
-         thispage = max;
      } catch (BooleanQuery.TooManyClauses e) {
 -        errorMsg = "<b>Error:</b> Too many results for wildcard!";
 +        errorMsg = "<div class=\"error\"><span class=\"prefix\">Error:</span> Too many results for wildcard.</div>";
      }
  
      // Bug #3900: Check if this is a search for a single term, and that term
-@@ -264,69 +273,65 @@
+@@ -268,76 +277,69 @@
  
          response.sendRedirect(url.toString());
      } else {
 -<div id="Masthead"></div>
 -<div id="bar">
 -    <table border="0" width="100%"><tr><td><a href="<%=context%>/" id="home">Home</a></td><td align="right"><%
--     {
+-    {
 -        String url = "search?";
--                url = url + (q == null ? "" : "&amp;q=" + Util.URIEncode(q)) +
--                 (defs == null ? "" : "&amp;defs=" + Util.URIEncode(defs)) +
--                 (refs == null ? "" : "&amp;refs=" + Util.URIEncode(refs)) +
--                 (path == null ? "" : "&amp;path=" + Util.URIEncode(path)) +
--                 (hist == null ? "" : "&amp;hist=" + Util.URIEncode(hist));
--         if (hasProjects) {
--             if (project!=null) {
--              url = url + "&amp;project=";
--              for (Iterator it = project.iterator(); it.hasNext();) {
--                  url = url + (project == null ? "" : Util.URIEncode((String) it.next()) + ",");
--              }
--             }
+-        url = url + (q == null ? "" : "q=" + Util.URIEncode(q) + "&amp;") +
+-                (defs == null ? "" : "defs=" + Util.URIEncode(defs) + "&amp;") +
+-                (refs == null ? "" : "refs=" + Util.URIEncode(refs) + "&amp;") +
+-                (path == null ? "" : "path=" + Util.URIEncode(path) + "&amp;") +
+-                (hist == null ? "" : "hist=" + Util.URIEncode(hist) + "&amp;");
+-        if (hasProjects) {
+-            if (project != null) {
+-                url = url + "project=";
+-                for (String projectName : project) {
+-                    url = url + (projectName == null ? "" : Util.URIEncode(projectName) + ",");
++        %><%@ include file="httpheader.jspf" %>
++<body class="page page-results">
++<div id="wrapper">
++    <%@ include file="pageheader.jspf" %>
++            </form>
++            <%
++            String url = "search?";
++            url = url + (q == null ? "" : "q=" + Util.URIEncode(q) + "&amp;") +
++                    (defs == null ? "" : "defs=" + Util.URIEncode(defs) + "&amp;") +
++                    (refs == null ? "" : "refs=" + Util.URIEncode(refs) + "&amp;") +
++                    (path == null ? "" : "path=" + Util.URIEncode(path) + "&amp;") +
++                    (hist == null ? "" : "hist=" + Util.URIEncode(hist) + "&amp;");
++            if (hasProjects) {
++                if (project != null) {
++                    url = url + "project=";
++                    for (String projectName : project) {
++                        url = url + (projectName == null ? "" : Util.URIEncode(projectName) + ",");
++                    }
++                    url = url + "&amp;";
+                 }
+-                url = url + "&amp;";
+             }
 -        }
 -         
 -        %>Sort by: <%        
--        url=url+("&amp;sort=");
+-        String sortUrl = url + "sort=";
 -        
 -        if (sort == null || RELEVANCY.equals(sort)) {
--        %><b>relevance</b> | <a href="<%=url+LASTMODTIME%>">last modified time</a> | <a href="<%=url+BY_PATH%>">path</a><%
+-        %><b>relevance</b> | <a href="<%=sortUrl+LASTMODTIME%>">last modified time</a> | <a href="<%=sortUrl+BY_PATH%>">path</a><%
 -        } else if (LASTMODTIME.equals(sort)) {
--           %><a href="<%=url+RELEVANCY%>">relevance</a> | <b>last modified time</b> | <a href="<%=url+BY_PATH%>">path</a><%
+-           %><a href="<%=sortUrl+RELEVANCY%>">relevance</a> | <b>last modified time</b> | <a href="<%=sortUrl+BY_PATH%>">path</a><%
 -        } else if (BY_PATH.equals(sort)) {
--           %><a href="<%=url+RELEVANCY%>">relevance</a> | <a href="<%=url+LASTMODTIME%>">last modified time</a> | <b>path</b><%
+-           %><a href="<%=sortUrl+RELEVANCY%>">relevance</a> | <a href="<%=sortUrl+LASTMODTIME%>">last modified time</a> | <b>path</b><%
 -        } else {
--           %><a href="<%=url+RELEVANCY%>">relevance</a> | <a href="<%=url+LASTMODTIME%>">last modified time</a> | <a href="<%=url+BY_PATH%>">path</a><%
+-           %><a href="<%=sortUrl+RELEVANCY%>">relevance</a> | <a href="<%=sortUrl+LASTMODTIME%>">last modified time</a> | <a href="<%=sortUrl+BY_PATH%>">path</a><%
 -        }
--      } %></td></tr></table>
+-    %></td></tr>
+-        <tr>
+-            <td colspan="2" align="right">
+-                <input id="max" type="text" value="<%=max%>" size="4" maxlength="4" onchange="window.location = '<%=url + "n="%>' + this.value">
+-                <label for="max">results per page</label>
+-            </td><%
+-    }%></tr></table>
 -</div>
 -<div id="menu">
 -   <%@ include file="menu.jspf"%>
 -            } else if (hits.length == 0) {
 -                File spellIndex = new File(env.getDataRootPath(), "spellIndex");
 -                File[] spellIndexes=null;
-+        %><%@ include file="httpheader.jspf" %>
-+<body class="page page-results">
-+<div id="wrapper">
-+    <%@ include file="pageheader.jspf" %>
-+            </form>
-+            <%
-+            String url = "search?";
-+            url = url + (q == null ? "" : "&amp;q=" + Util.URIEncode(q)) +
-+                (defs == null ? "" : "&amp;defs=" + Util.URIEncode(defs)) +
-+                (refs == null ? "" : "&amp;refs=" + Util.URIEncode(refs)) +
-+                (path == null ? "" : "&amp;path=" + Util.URIEncode(path)) +
-+                (hist == null ? "" : "&amp;hist=" + Util.URIEncode(hist));
-+            if (hasProjects) {
-+                if (project!=null) {
-+                    url = url + "&amp;project=";
-+                    for (Iterator it = project.iterator(); it.hasNext();) {
-+                        url = url + (project == null ? "" : Util.URIEncode((String) it.next()) + ",");
-+                    }
-+                }
-+            }
 +            %><div class="sortMenu"><span class="menuHeader">Sort by:</span><%
-+            url=url+("&amp;sort=");
++            String sortUrl = url + "sort=";
+ 
 +            if (sort == null || RELEVANCY.equals(sort)) {
-+                %><span class="sortOption sortOptionRelevance selected">relevance</span> <a href="<%=url+LASTMODTIME%>" class="sortOption sortOptionTime">last modified time</a> <a href="<%=url+BY_PATH%>" class="sortOption sortOptionPath">path</a><%
++                %><span class="sortOption sortOptionRelevance selected">relevance</span> <a href="<%=sortUrl+LASTMODTIME%>" class="sortOption sortOptionTime">last modified time</a> <a href="<%=sortUrl+BY_PATH%>" class="sortOption sortOptionPath">path</a><%
 +            } else if (LASTMODTIME.equals(sort)) {
-+                %><a href="<%=url+RELEVANCY%>" class="sortOption sortOptionRelevance">relevance</a> <span class="sortOption sortOptionTime selected">last modified time</span> <a href="<%=url+BY_PATH%>" class="sortOption sortOptionPath">path</a><%
++                %><a href="<%=sortUrl+RELEVANCY%>" class="sortOption sortOptionRelevance">relevance</a> <span class="sortOption sortOptionTime selected">last modified time</span> <a href="<%=sortUrl+BY_PATH%>" class="sortOption sortOptionPath">path</a><%
 +            } else if (BY_PATH.equals(sort)) {
-+                %><a href="<%=url+RELEVANCY%>" class="sortOption sortOptionRelevance">relevance</a> <a href="<%=url+LASTMODTIME%>" class="sortOption sortOptionTime">last modified time</a> <span class="sortOption sortOptionPath selected">path</span><%
++                %><a href="<%=sortUrl+RELEVANCY%>" class="sortOption sortOptionRelevance">relevance</a> <a href="<%=sortUrl+LASTMODTIME%>" class="sortOption sortOptionTime">last modified time</a> <span class="sortOption sortOptionPath selected">path</span><%
 +            } else {
-+                %><a href="<%=url+RELEVANCY%>" class="sortOption sortOptionRelevance">relevance</a> <a href="<%=url+LASTMODTIME%>" class="sortOption sortOptionTime">last modified time</a> <a href="<%=url+BY_PATH%>" class="sortOption sortOptionPath">path</a><%
++                %><a href="<%=sortUrl+RELEVANCY%>" class="sortOption sortOptionRelevance">relevance</a> <a href="<%=sortUrl+LASTMODTIME%>" class="sortOption sortOptionTime">last modified time</a> <a href="<%=sortUrl+BY_PATH%>" class="sortOption sortOptionPath">path</a><%
 +            }%></div>
++            <div class="pageSize">
++                <input id="max" type="text" value="<%=max%>" maxlength="4" onchange="window.location = '<%=url + "n="%>' + this.value">                                                                                                                                                                <label for="max">results per page</label>
++            </div>
 +        </div>
 +    </div>
-+    
++
 +    <div id="content">
 +        <div id="contentBody">
 +            <h1>Inspector Gadget search results</h1>
 +        } else if (hits.length == 0) {
 +            File spellIndex = new File(env.getDataRootPath(), "spellIndex");
 +            File[] spellIndexes=null;
- 
                  if (RuntimeEnvironment.getInstance().hasProjects()) {
 -                 if (project.size() > 1) { //more projects
 -                    spellIndexes = new File[project.size()];                    
 -                    for (String proj : project) {                        
 -                        spellIndexes[ii++] = new File(spellIndex,proj);
 +                    if (project.size() > 1) { //more projects
-+                        spellIndexes = new File[project.size()];                    
++                        spellIndexes = new File[project.size()];
 +                        int ii = 0;
 +                        //TODO might need to rewrite to Project instead of String , need changes in projects.jspf too
-+                        for (String proj : project) {                        
++                        for (String proj : project) {
 +                            spellIndexes[ii++] = new File(spellIndex,proj);
 +                        }
 +                    } else { // just 1 project selected
-+                        spellIndex = new File(spellIndex, project.get(0));                    
++                        spellIndex = new File(spellIndex, project.get(0));
                      }
 -                 } else { // just 1 project selected
 -                    spellIndex = new File(spellIndex, project.get(0));                    
                  }
  
                  int count=1;
-@@ -334,14 +339,14 @@
- 
-                 for (int idx = 0; idx < count; idx++) {
-    
--                if (spellIndexes!=null) spellIndex = spellIndexes[idx];
-+                    if (spellIndexes!=null) spellIndex = spellIndexes[idx];
+@@ -349,12 +351,12 @@
+                         spellIndex = spellIndexes[idx];
+                     }
                  
 -                 if (spellIndex.exists()) {
 -                    FSDirectory spellDirectory = FSDirectory.open(spellIndex);
 -                    SpellChecker checker = new SpellChecker(spellDirectory);
+-
+-                    Date sstart = new Date();
+-                    boolean printHeader = true;
 +                    if (spellIndex.exists()) {
 +                        FSDirectory spellDirectory = FSDirectory.open(spellIndex);
 +                        SpellChecker checker = new SpellChecker(spellDirectory);
- 
--                    Date sstart = new Date();
--                    boolean printHeader = true;
++    
 +                        Date sstart = new Date();
 +                        boolean printHeader = true;
                          String[] toks;
                          if(q != null) {
                              toks = q.split("[\t ]+");
-@@ -351,10 +356,10 @@
+@@ -364,10 +366,10 @@
                                          String[] ret = checker.suggestSimilar(toks[j].toLowerCase(), 5);
                                          for(int i = 0;i < ret.length; i++) {
                                              if (printHeader) {
                                          }
                                      }
                                  }
-@@ -368,14 +373,14 @@
+@@ -381,14 +383,14 @@
                                          String[] ret = checker.suggestSimilar(toks[j].toLowerCase(), 5);
                                          for(int i = 0;i < ret.length; i++) {
                                              if (printHeader) {
                          }
                          //TODO it seems the only true spellchecker is for below field, see IndexDatabase createspellingsuggestions ...
                          if(defs != null) {
-@@ -386,10 +391,10 @@
+@@ -399,10 +401,10 @@
                                          String[] ret = checker.suggestSimilar(toks[j].toLowerCase(), 5);
                                          for(int i = 0;i < ret.length; i++) {
                                              if (printHeader) {
                                          }
                                      }
                                  }
-@@ -400,123 +405,129 @@
+@@ -413,124 +415,129 @@
                          }
                          spellDirectory.close();
                          checker.close();
 -		</p><%
 -            } else { // We have a lots of results to show
 -                StringBuilder slider = null;
--                if ( max < totalHits) {
--                    if((start + max) < totalHits) {
--                        thispage = max;
+-                if (totalHits > max) {
+-                    if (totalHits > (start + max)) {
+-                        hitsOnThisPage = max;
 -                    } else {
--                        thispage = totalHits - start;
+-                        hitsOnThisPage = totalHits - start;
 +                %><p>Your search <span class="code code-search"><%=query.toString()%></span> did not match any files.</p>
 +                    <p>Suggestions:</p>
 +                    <ul>
 +                <%
 +                    } else { // We have a lots of results to show
 +                        StringBuilder slider = null;
-+                        if (max < totalHits) {
-+                            if((start + max) < totalHits) {
-+                                thispage = max;
++                        if (totalHits > max) {
++                            if(totalHits > (start + max)) {
++                                hitsOnThisPage = max;
 +                            } else {
-+                                thispage = totalHits - start;
++                                hitsOnThisPage = totalHits - start;
 +                            }
 +                            String urlp = (q == null ? "" : "&amp;q=" + Util.URIEncode(q)) +
 +                                    (defs == null ? "" : "&amp;defs=" + Util.URIEncode(defs)) +
 +                            }
 +                            slider.append("</div>");
 +                        } else {
-+                            thispage = totalHits - start;      // set the max index to max or last
++                            hitsOnThisPage = totalHits - start;      // set the max index to max or last
 +                        }
 +                %><p>Searched <span class="code code-search"><%=query.toString()%></span><br/>
-+                    Results <strong><%=start+1%> - <%=thispage+start%></strong> of <strong><%=totalHits%></strong>, sorted by <%=sortName%></p>
++                    Results <strong><%=start+1%> - <%=hitsOnThisPage+start%></strong> of <strong><%=totalHits%></strong>, sorted by <%=sortName%></p>
 +                    <p>Completed in <%=(new Date()).getTime() - starttime.getTime()%> milliseconds.</p>
 +                    <%=slider != null ? slider.toString() : "" %>
 +		<table id="results"><%
-+                
++
 +                        Context sourceContext = null;
 +                        Summarizer summer = null;
 +                        if (query != null) {
 +                                    summer = new Summarizer(query,
 +                                                            new CompatibleAnalyser());
 +                            } catch (Exception e) {
-+                        
++
 +                            }
 +                        }
-+                
++
 +                        HistoryContext historyContext = null;
 +                        try {
 +                            historyContext = new HistoryContext(query);
 +                        } catch (Exception e) {
 +                        }
 +                        //TODO also fix the way what and how it is passed to prettyprint, can improve performance! SearchEngine integration is really needed here.
-+                        Results.prettyPrintHTML(searcher,hits, start, start+thispage,
++                        Results.prettyPrintHTML(searcher,hits, start, start+hitsOnThisPage,
 +                                out,
 +                                sourceContext, historyContext, summer,
 +                                context,
 -                        label++;
 -                    }
 -                } else {
--                    thispage = totalHits - start;      // set the max index to max or last
+-                    hitsOnThisPage = totalHits - start;      // set the max index to max or last
 -                }
 -		%>&nbsp; &nbsp; Searched <b><%=query.toString()%></b> (Results <b><%=start+1%> -
--		<%=thispage+start%></b> of <b><%=totalHits%></b>) sorted by <%=sort%> <p><%=slider != null ?
+-		<%=hitsOnThisPage+start%></b> of <b><%=totalHits%></b>) sorted by <%=sort%> <p><%=slider != null ?
 -                    slider.toString(): ""%></p>
 -		<table width="100%" cellpadding="3" cellspacing="0" border="0"><%
 -                
 -                    try{
 -                        sourceContext =
 -                                new Context(query, queryBuilder.getQueries());
--                        if(sourceContext != null)
+-                        if (sourceContext != null) {
 -                            summer = new Summarizer(query,
--                                                    new CompatibleAnalyser());
+-                                    new CompatibleAnalyser());
+-                        }
 -                    } catch (Exception e) {
 -                        
 -                    }
 -                } catch (Exception e) {
 -                }
 -                //TODO also fix the way what and how it is passed to prettyprint, can improve performance! SearchEngine integration is really needed here.
--                Results.prettyPrintHTML(searcher,hits, start, start+thispage,
+-                Results.prettyPrintHTML(searcher,hits, start, start+hitsOnThisPage,
 -                        out,
 -                        sourceContext, historyContext, summer,
 -                        context,
 -}
 -%>
 +            if (searcher != null) {
-+                searcher.close();
++                searcher.close();                            
 +            }
 +        } else { // Entry page show the map
 +            response.sendRedirect(context + "/index.jsp");
+# HG changeset patch
+# Parent 4319a2f47cea0e594df129b160daa0f6e8005ddb
+# User J. Ryan Stinnett <jryans@gmail.com>
+# Date 1295356647 21600
+
+Adds explicit page size control
+
+diff --git a/web/search.jsp b/web/search.jsp
+--- a/web/search.jsp
++++ b/web/search.jsp
+@@ -19,31 +19,31 @@
+ Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved.
+ 
+ --%><%@ page import = "javax.servlet.*,
+-java.lang.Integer,
+-javax.servlet.http.*,
+-java.util.Hashtable,
+-java.util.Vector,
+-java.util.Date,
+-java.util.ArrayList,
+-java.util.List,
+-java.lang.*,
+-java.io.*,
+-java.io.StringReader,
+-org.opensolaris.opengrok.analysis.*,
+-org.opensolaris.opengrok.index.IndexDatabase,
+-org.opensolaris.opengrok.search.*,
+-org.opensolaris.opengrok.web.*,
+-org.opensolaris.opengrok.search.context.*,
+-org.opensolaris.opengrok.configuration.*,
+-org.apache.lucene.search.spell.LuceneDictionary,
+-org.apache.lucene.search.spell.SpellChecker,
+-org.apache.lucene.search.SortField,
+-org.apache.lucene.search.TopScoreDocCollector,
+-org.apache.lucene.store.FSDirectory,
+-org.apache.lucene.analysis.*,
+-org.apache.lucene.document.*,
+-org.apache.lucene.search.*,
+-org.apache.lucene.queryParser.*"
++                       java.lang.Integer,
++                       javax.servlet.http.*,
++                       java.util.Hashtable,
++                       java.util.Vector,
++                       java.util.Date,
++                       java.util.ArrayList,
++                       java.util.List,
++                       java.lang.*,
++                       java.io.*,
++                       java.io.StringReader,
++                       org.opensolaris.opengrok.analysis.*,
++                       org.opensolaris.opengrok.index.IndexDatabase,
++                       org.opensolaris.opengrok.search.*,
++                       org.opensolaris.opengrok.web.*,
++                       org.opensolaris.opengrok.search.context.*,
++                       org.opensolaris.opengrok.configuration.*,
++                       org.apache.lucene.search.spell.LuceneDictionary,
++                       org.apache.lucene.search.spell.SpellChecker,
++                       org.apache.lucene.search.SortField,
++                       org.apache.lucene.search.TopScoreDocCollector,
++                       org.apache.lucene.store.FSDirectory,
++                       org.apache.lucene.analysis.*,
++                       org.apache.lucene.document.*,
++                       org.apache.lucene.search.*,
++                       org.apache.lucene.queryParser.*"
+ %><%@ page session="false" %><%@ page errorPage="error.jsp" %><%--
+ --%><%@ include file="projects.jspf" %><%
+ Date starttime = new Date();
+@@ -91,12 +91,24 @@
+ //List<org.apache.lucene.document.Document> docs=new ArrayList<org.apache.lucene.document.Document>();
+ String errorMsg = null;
+ 
+-if( q!= null && q.equals("")) q = null;
+-if( defs != null && defs.equals("")) defs = null;
+-if( refs != null && refs.equals("")) refs = null;
+-if( hist != null && hist.equals("")) hist = null;
+-if( path != null && path.equals("")) path = null;
+-if (project != null && project.size()<1) project = null;
++    if (q != null && q.equals("")) {
++        q = null;
++    }
++    if (defs != null && defs.equals("")) {
++        defs = null;
++    }
++    if (refs != null && refs.equals("")) {
++        refs = null;
++    }
++    if (hist != null && hist.equals("")) {
++        hist = null;
++    }
++    if (path != null && path.equals("")) {
++        path = null;
++    }
++    if (project != null && project.size() < 1) {
++        project = null;
++    }
+ 
+ if (q != null || defs != null || refs != null || hist != null || path != null) {
+     Searcher searcher = null;   //the searcher used to open/search the index
+@@ -266,35 +278,42 @@
+ <div id="Masthead"></div>
+ <div id="bar">
+     <table border="0" width="100%"><tr><td><a href="<%=context%>/" id="home">Home</a></td><td align="right"><%
+-     {
++    {
+         String url = "search?";
+-                url = url + (q == null ? "" : "&amp;q=" + Util.URIEncode(q)) +
+-                 (defs == null ? "" : "&amp;defs=" + Util.URIEncode(defs)) +
+-                 (refs == null ? "" : "&amp;refs=" + Util.URIEncode(refs)) +
+-                 (path == null ? "" : "&amp;path=" + Util.URIEncode(path)) +
+-                 (hist == null ? "" : "&amp;hist=" + Util.URIEncode(hist));
+-         if (hasProjects) {
+-             if (project!=null) {
+-              url = url + "&amp;project=";
+-              for (Iterator it = project.iterator(); it.hasNext();) {
+-                  url = url + (project == null ? "" : Util.URIEncode((String) it.next()) + ",");
+-              }
+-             }
++        url = url + (q == null ? "" : "q=" + Util.URIEncode(q) + "&amp;") +
++                (defs == null ? "" : "defs=" + Util.URIEncode(defs) + "&amp;") +
++                (refs == null ? "" : "refs=" + Util.URIEncode(refs) + "&amp;") +
++                (path == null ? "" : "path=" + Util.URIEncode(path) + "&amp;") +
++                (hist == null ? "" : "hist=" + Util.URIEncode(hist) + "&amp;");
++        if (hasProjects) {
++            if (project != null) {
++                url = url + "project=";
++                for (String projectName : project) {
++                    url = url + (projectName == null ? "" : Util.URIEncode(projectName) + ",");
++                }
++                url = url + "&amp;";
++            }
+         }
+          
+         %>Sort by: <%        
+-        url=url+("&amp;sort=");
++        String sortUrl = url + "sort=";
+         
+         if (sort == null || RELEVANCY.equals(sort)) {
+-        %><b>relevance</b> | <a href="<%=url+LASTMODTIME%>">last modified time</a> | <a href="<%=url+BY_PATH%>">path</a><%
++        %><b>relevance</b> | <a href="<%=sortUrl+LASTMODTIME%>">last modified time</a> | <a href="<%=sortUrl+BY_PATH%>">path</a><%
+         } else if (LASTMODTIME.equals(sort)) {
+-           %><a href="<%=url+RELEVANCY%>">relevance</a> | <b>last modified time</b> | <a href="<%=url+BY_PATH%>">path</a><%
++           %><a href="<%=sortUrl+RELEVANCY%>">relevance</a> | <b>last modified time</b> | <a href="<%=sortUrl+BY_PATH%>">path</a><%
+         } else if (BY_PATH.equals(sort)) {
+-           %><a href="<%=url+RELEVANCY%>">relevance</a> | <a href="<%=url+LASTMODTIME%>">last modified time</a> | <b>path</b><%
++           %><a href="<%=sortUrl+RELEVANCY%>">relevance</a> | <a href="<%=sortUrl+LASTMODTIME%>">last modified time</a> | <b>path</b><%
+         } else {
+-           %><a href="<%=url+RELEVANCY%>">relevance</a> | <a href="<%=url+LASTMODTIME%>">last modified time</a> | <a href="<%=url+BY_PATH%>">path</a><%
++           %><a href="<%=sortUrl+RELEVANCY%>">relevance</a> | <a href="<%=sortUrl+LASTMODTIME%>">last modified time</a> | <a href="<%=sortUrl+BY_PATH%>">path</a><%
+         }
+-      } %></td></tr></table>
++    %></td></tr>
++        <tr>
++            <td colspan="2" align="right">
++                <input id="max" type="text" value="<%=max%>" size="4" maxlength="4" onchange="window.location = '<%=url + "n="%>' + this.value">
++                <label for="max">results per page</label>
++            </td><%
++    }%></tr></table>
+ </div>
+ <div id="menu">
+    <%@ include file="menu.jspf"%>
+@@ -325,8 +344,10 @@
+                 if (spellIndexes!=null) {count=spellIndexes.length;}                
+ 
+                 for (int idx = 0; idx < count; idx++) {
+-   
+-                if (spellIndexes!=null) spellIndex = spellIndexes[idx];
++
++                    if (spellIndexes != null) {
++                        spellIndex = spellIndexes[idx];
++                    }
+                 
+                  if (spellIndex.exists()) {
+                     FSDirectory spellDirectory = FSDirectory.open(spellIndex);
+@@ -466,9 +487,10 @@
+                     try{
+                         sourceContext =
+                                 new Context(query, queryBuilder.getQueries());
+-                        if(sourceContext != null)
++                        if (sourceContext != null) {
+                             summer = new Summarizer(query,
+-                                                    new CompatibleAnalyser());
++                                    new CompatibleAnalyser());
++                        }
+                     } catch (Exception e) {
+                         
+                     }

pagination-cleanup

+# HG changeset patch
+# Parent 4dcd76ff7b5a03f5c7e5ceb42a0ab3e60d6e6197
+# User J. Ryan Stinnett <jryans@gmail.com>
+# Date 1295353553 21600
+
+Clean up pagination code
+
+diff --git a/src/org/opensolaris/opengrok/configuration/Configuration.java b/src/org/opensolaris/opengrok/configuration/Configuration.java
+--- a/src/org/opensolaris/opengrok/configuration/Configuration.java
++++ b/src/org/opensolaris/opengrok/configuration/Configuration.java
+@@ -22,6 +22,10 @@
+  */
+ package org.opensolaris.opengrok.configuration;
+ 
++import org.opensolaris.opengrok.history.RepositoryInfo;
++import org.opensolaris.opengrok.index.Filter;
++import org.opensolaris.opengrok.index.IgnoredNames;
++
+ import java.beans.XMLDecoder;
+ import java.beans.XMLEncoder;
+ import java.io.BufferedInputStream;
+@@ -39,9 +43,6 @@
+ import java.util.HashSet;
+ import java.util.List;
+ import java.util.Set;
+-import org.opensolaris.opengrok.history.RepositoryInfo;
+-import org.opensolaris.opengrok.index.Filter;
+-import org.opensolaris.opengrok.index.IgnoredNames;
+ 
+ /**
+  * Placeholder class for all configuration variables. Due to the multithreaded
+@@ -91,6 +92,7 @@
+     private boolean compressXref;
+     private boolean indexVersionedFilesOnly;
+     private int hitsPerPage;
++    private int maxHitsPerPage;
+     private int cachePages;
+     private String databaseDriver;
+     private String databaseUrl;
+@@ -138,6 +140,7 @@
+         setCompressXref(true);
+         setIndexVersionedFilesOnly(false);
+         setHitsPerPage(25);
++        setMaxHitsPerPage(50);
+         setCachePages(5);
+         setScanningDepth(3); // default depth of scanning for repositories
+         setAllowedSymlinks(new HashSet<String>());
+@@ -167,6 +170,14 @@
+         this.hitsPerPage = hitsPerPage;
+     }
+ 
++    public int getMaxHitsPerPage() {
++        return maxHitsPerPage;
++    }
++
++    public void setMaxHitsPerPage(int maxHitsPerPage) {
++        this.maxHitsPerPage = maxHitsPerPage;
++    }
++
+     /**
+      * Should the history log be cached?
+      * @return {@code true} if a {@code HistoryCache} implementation should
+diff --git a/src/org/opensolaris/opengrok/configuration/RuntimeEnvironment.java b/src/org/opensolaris/opengrok/configuration/RuntimeEnvironment.java
+--- a/src/org/opensolaris/opengrok/configuration/RuntimeEnvironment.java
++++ b/src/org/opensolaris/opengrok/configuration/RuntimeEnvironment.java
+@@ -272,6 +272,10 @@
+         threadConfig.get().setHitsPerPage(hitsPerPage);
+     }
+ 
++    public int getMaxHitsPerPage() {
++        return threadConfig.get().getMaxHitsPerPage();
++    }
++
+     /**
+      * Validate that I have a Exuberant ctags program I may use
+      * @return true if success, false otherwise
+diff --git a/web/search.jsp b/web/search.jsp
+--- a/web/search.jsp
++++ b/web/search.jsp
+@@ -99,23 +99,19 @@
+ if (project != null && project.size()<1) project = null;
+ 
+ if (q != null || defs != null || refs != null || hist != null || path != null) {
+-    Searcher searcher = null;		    //the searcher used to open/search the index
+-    TopScoreDocCollector collector=null;         // the collector used
+-    ScoreDoc[] hits = null;                 // list of documents which result from the query
++    Searcher searcher = null;   //the searcher used to open/search the index
++    ScoreDoc[] hits = null;     //list of documents which result from the query
+     Query query = null;         //the Query created by the QueryBuilder
+-    boolean allCollected=false;
+-    int totalHits=0;
++    int totalHits = 0;
+     
+-    int start = 0;		       //the first index displayed on this page
++    int hitsPerPage = RuntimeEnvironment.getInstance().getHitsPerPage();
++    int cachePages = RuntimeEnvironment.getInstance().getCachePages();
++    final boolean docsScoredInOrder = false;
++
++    int start = 0;          //the first index displayed on this page
+     //TODO deprecate max this and merge with paging and param n - TEST needed
+-    //int max    = 25;			//the maximum items displayed on this page
+-    int max=RuntimeEnvironment.getInstance().getHitsPerPage();
+-
+-    int hitsPerPage = RuntimeEnvironment.getInstance().getHitsPerPage();
+-    int cachePages= RuntimeEnvironment.getInstance().getCachePages();
+-    final boolean docsScoredInOrder=false;
+-
+-    int thispage = 0;			    //used for the for/next either max or
++    int max = hitsPerPage;  //the maximum items displayed on this page
++    int hitsOnThisPage;     //used for the for/next either max or
+ 
+     QueryBuilder queryBuilder =
+             new QueryBuilder()
+@@ -131,14 +127,25 @@
+         if(!data_root.isDirectory()) {
+             throw new Exception("DATA_ROOT parameter in web.xml does not exist or is not a directory!");
+         }
+-        //String date = request.getParameter("date");
+-        try {
+-            //TODO merge paging hitsPerPage with parameter n (has to reflect the search if changed so proper number is cached first time)
+-            start = Integer.parseInt(request.getParameter("start"));	//parse the max results first
+-            max = Integer.parseInt(request.getParameter("n"));      //then the start index
+-            if(max < 0 || (max % 10 != 0) || max > 50) max = 25;
+-            if(start < 0 ) start = 0;
+-        } catch (Exception e) {  }
++
++        //TODO merge paging hitsPerPage with parameter n (has to reflect the search if changed so proper number is cached first time)
++        String maxParam = request.getParameter("n");
++        if (maxParam != null) {
++            max = Integer.parseInt(maxParam);
++            if (max < 0) {
++                max = 0;
++            } else if (max > RuntimeEnvironment.getInstance().getMaxHitsPerPage()) {
++                max = RuntimeEnvironment.getInstance().getMaxHitsPerPage();
++            }
++        }
++
++        String startParam = request.getParameter("start");
++        if (startParam != null) {
++            start = Integer.parseInt(startParam);
++            if (start < 0) {
++                start = 0;
++            }
++        }
+ 
+         query = queryBuilder.build();
+         
+@@ -172,49 +179,34 @@
+         } else { //no project setup
+             FSDirectory dir = FSDirectory.open(root);
+             searcher = new IndexSearcher(dir);
+-            }
++        }
+ 
+         //TODO check if below is somehow reusing sessions so we don't requery again and again, I guess 2min timeout sessions could be usefull, since you click on the next page within 2mins, if not, then wait ;)
+-        if (errorMsg == null) {                        
+-            collector = TopScoreDocCollector.create(hitsPerPage*cachePages,docsScoredInOrder);
+-            if (LASTMODTIME.equals(sort)) {
+-                Sort sortf = new Sort(new SortField("date",SortField.STRING,true));
+-                TopFieldDocs fdocs=searcher.search(query, null,hitsPerPage*cachePages, sortf);
+-                totalHits=fdocs.totalHits;
+-                if (start>=hitsPerPage*cachePages && !allCollected) { //fetch ALL results only if above cachePages
+-                 fdocs=searcher.search(query, null, totalHits, sortf);
+-                 allCollected=true;
++        if (errorMsg == null) {
++            if (RELEVANCY.equals(sort)) {
++                TopScoreDocCollector collector = TopScoreDocCollector.create(hitsPerPage * cachePages, docsScoredInOrder);
++                searcher.search(query, collector);
++                totalHits = collector.getTotalHits();
++                if (start >= hitsPerPage * cachePages) { //fetch ALL results only if above cachePages
++                    collector = TopScoreDocCollector.create(totalHits, docsScoredInOrder);
++                    searcher.search(query, collector);
+                 }
+-                hits = fdocs.scoreDocs; 
+-            } else if (BY_PATH.equals(sort)) {
+-                Sort sortf = new Sort(S_BY_PATH);
+-                TopFieldDocs fdocs=searcher.search(query, null,hitsPerPage*cachePages, sortf);
+-                totalHits=fdocs.totalHits;
+-                if (start>=hitsPerPage*cachePages && !allCollected) { //fetch ALL results only if above cachePages
+-                 fdocs=searcher.search(query, null,totalHits, sortf);
+-                 allCollected=true;
++                hits = collector.topDocs().scoreDocs;
++            } else {
++                Sort sortField;
++                if (LASTMODTIME.equals(sort)) {
++                    sortField = new Sort(new SortField("date", SortField.STRING, true));
++                } else { // by path
++                    sortField = new Sort(S_BY_PATH);
++                }
++                TopFieldDocs fdocs = searcher.search(query, null, hitsPerPage * cachePages, sortField);
++                totalHits = fdocs.totalHits;
++                if (start >= hitsPerPage * cachePages) { //fetch ALL results only if above cachePages
++                    fdocs = searcher.search(query, null, totalHits, sortField);
+                 }
+                 hits = fdocs.scoreDocs;
+-            } else {
+-                searcher.search(query,collector);
+-                totalHits=collector.getTotalHits();
+-                if (start>=hitsPerPage*cachePages && !allCollected) { //fetch ALL results only if above cachePages
+-                 collector = TopScoreDocCollector.create(totalHits,docsScoredInOrder);
+-                 searcher.search(query,collector);
+-                 allCollected=true;
+-                }
+-                hits=collector.topDocs().scoreDocs;
+             }
+-
+-            //below will get all the documents
+-//            for (int i = 0; i < hits.length; i++) {
+-//              int docId = hits[i].doc;
+-//              Document d = searcher.doc(docId);
+-//              docs.add(d);
+-//            }
+-
+         }
+-        thispage = max;
+     } catch (BooleanQuery.TooManyClauses e) {
+         errorMsg = "<b>Error:</b> Too many results for wildcard!";
+     } catch (ParseException e) {
+@@ -414,11 +406,11 @@
+ 		</p><%
+             } else { // We have a lots of results to show
+                 StringBuilder slider = null;
+-                if ( max < totalHits) {
+-                    if((start + max) < totalHits) {
+-                        thispage = max;
++                if (totalHits > max) {
++                    if (totalHits > (start + max)) {
++                        hitsOnThisPage = max;
+                     } else {
+-                        thispage = totalHits - start;
++                        hitsOnThisPage = totalHits - start;
+                     }
+                     String urlp = (q == null ? "" : "&amp;q=" + Util.URIEncode(q)) +
+                             (defs == null ? "" : "&amp;defs=" + Util.URIEncode(defs)) +
+@@ -461,10 +453,10 @@
+                         label++;
+                     }
+                 } else {
+-                    thispage = totalHits - start;      // set the max index to max or last
++                    hitsOnThisPage = totalHits - start;      // set the max index to max or last
+                 }
+ 		%>&nbsp; &nbsp; Searched <b><%=query.toString()%></b> (Results <b><%=start+1%> -
+-		<%=thispage+start%></b> of <b><%=totalHits%></b>) sorted by <%=sort%> <p><%=slider != null ?
++		<%=hitsOnThisPage+start%></b> of <b><%=totalHits%></b>) sorted by <%=sort%> <p><%=slider != null ?
+                     slider.toString(): ""%></p>
+ 		<table width="100%" cellpadding="3" cellspacing="0" border="0"><%
+                 
+@@ -493,7 +485,7 @@
+                 } catch (Exception e) {
+                 }
+                 //TODO also fix the way what and how it is passed to prettyprint, can improve performance! SearchEngine integration is really needed here.
+-                Results.prettyPrintHTML(searcher,hits, start, start+thispage,
++                Results.prettyPrintHTML(searcher,hits, start, start+hitsOnThisPage,
+                         out,
+                         sourceContext, historyContext, summer,
+                         context,
 check-js-symbol-list
 focus-full-text
 sort-directories-first
+pagination-cleanup
+page-size
 bazaarvoice-customizations