Index: src/site/src/documentation/content/xdocs/site.xml
===================================================================
--- src/site/src/documentation/content/xdocs/site.xml	(revision 792381)
+++ src/site/src/documentation/content/xdocs/site.xml	(working copy)
@@ -54,6 +54,7 @@
 		    <javadoc-contrib-bdb-je label="Bdb-je" href="ext:javadocs-contrib-bdb-je"/>
 		    <javadoc-contrib-benchmark label="Benchmark" href="ext:javadocs-contrib-benchmark"/>
         <javadoc-contrib-collation label="Collation" href="ext:javadocs-contrib-collation"/>
+		    <javadoc-contrib-fast-vector-highlighter label="Fast Vector Highlighter" href="ext:javadocs-contrib-fast-vector-highlighter"/>
 		    <javadoc-contrib-highlighter label="Highlighter" href="ext:javadocs-contrib-highlighter"/>
 		    <javadoc-contrib-instantiated label="Instantiated" href="ext:javadocs-contrib-instantiated"/>
 		    <javadoc-contrib-lucli label="Lucli" href="ext:javadocs-contrib-lucli"/>
@@ -103,7 +104,8 @@
 	<javadocs-contrib-bdb href="api/contrib-bdb/index.html"/>
 	<javadocs-contrib-bdb-je href="api/contrib-bdb-je/index.html"/>
 	<javadocs-contrib-benchmark href="api/contrib-benchmark/index.html"/>
-  <javadocs-contrib-collation href="api/contrib-collation/index.html"/>
+	<javadocs-contrib-collation href="api/contrib-collation/index.html"/>
+	<javadocs-contrib-fast-vector-highlighter href="api/contrib-fast-vector-highlighter/index.html"/>
 	<javadocs-contrib-highlighter href="api/contrib-highlighter/index.html"/>
 	<javadocs-contrib-instantiated href="api/contrib-instantiated/index.html"/>
 	<javadocs-contrib-lucli href="api/contrib-lucli/index.html"/>
Index: src/site/src/documentation/content/xdocs/lucene-sandbox/index.xml
===================================================================
--- src/site/src/documentation/content/xdocs/lucene-sandbox/index.xml	(revision 792381)
+++ src/site/src/documentation/content/xdocs/lucene-sandbox/index.xml	(working copy)
@@ -104,6 +104,14 @@
                         repository for the Highlighter contribution.</a>
             </section>
 
+            <section id="Fast Vector Highlighter"><title>Fast Vector Highlighter</title>
+                <p>
+                  An alternative set of classes for highlighting matching terms in search results.
+                </p>
+                    <a href="http://svn.apache.org/repos/asf/lucene/java/trunk/contrib/fast-vector-highlighter/">The
+                        repository for the Fast Vector Highlighter contribution.</a>
+            </section>
+
             <section id="Javascript Query Constructor"><title>Javascript Query Constructor</title>
                 <p>
                     Javascript library to support client-side query-building. Provides support for a user interface similar to
Index: docs/benchmarks.html
===================================================================
--- docs/benchmarks.html	(revision 792381)
+++ docs/benchmarks.html	(working copy)
@@ -50,7 +50,7 @@
     +-->
 <div class="searchbox">
 <form action="http://search.lucidimagination.com/p:lucene" method="get" class="roundtopsmall">
-<input onFocus="getBlank (this, 'Search the site with google');" size="25" name="q" id="query" type="text" value="Search the site with google">&nbsp; 
+<input onFocus="getBlank (this, 'Search the site with Lucene');" size="25" name="q" id="query" type="text" value="Search the site with Lucene">&nbsp; 
                     <input name="Search" value="Search" type="submit">
 </form>
 <div style="position: relative; top: -5px; left: -10px">Powered by <a href="http://www.lucidimagination.com" style="color: #033268">Lucid Imagination</a>
@@ -142,6 +142,9 @@
 <a href="api/contrib-collation/index.html">Collation</a>
 </div>
 <div class="menuitem">
+<a href="api/contrib-fast-vector-highlighter/index.html">Fast Vector Highlighter</a>
+</div>
+<div class="menuitem">
 <a href="api/contrib-highlighter/index.html">Highlighter</a>
 </div>
 <div class="menuitem">
Index: docs/demo.html
===================================================================
--- docs/demo.html	(revision 792381)
+++ docs/demo.html	(working copy)
@@ -52,7 +52,7 @@
     +-->
 <div class="searchbox">
 <form action="http://search.lucidimagination.com/p:lucene" method="get" class="roundtopsmall">
-<input onFocus="getBlank (this, 'Search the site with google');" size="25" name="q" id="query" type="text" value="Search the site with google">&nbsp; 
+<input onFocus="getBlank (this, 'Search the site with Lucene');" size="25" name="q" id="query" type="text" value="Search the site with Lucene">&nbsp; 
                     <input name="Search" value="Search" type="submit">
 </form>
 <div style="position: relative; top: -5px; left: -10px">Powered by <a href="http://www.lucidimagination.com" style="color: #033268">Lucid Imagination</a>
@@ -144,6 +144,9 @@
 <a href="api/contrib-collation/index.html">Collation</a>
 </div>
 <div class="menuitem">
+<a href="api/contrib-fast-vector-highlighter/index.html">Fast Vector Highlighter</a>
+</div>
+<div class="menuitem">
 <a href="api/contrib-highlighter/index.html">Highlighter</a>
 </div>
 <div class="menuitem">
Index: docs/gettingstarted.html
===================================================================
--- docs/gettingstarted.html	(revision 792381)
+++ docs/gettingstarted.html	(working copy)
@@ -52,7 +52,7 @@
     +-->
 <div class="searchbox">
 <form action="http://search.lucidimagination.com/p:lucene" method="get" class="roundtopsmall">
-<input onFocus="getBlank (this, 'Search the site with google');" size="25" name="q" id="query" type="text" value="Search the site with google">&nbsp; 
+<input onFocus="getBlank (this, 'Search the site with Lucene');" size="25" name="q" id="query" type="text" value="Search the site with Lucene">&nbsp; 
                     <input name="Search" value="Search" type="submit">
 </form>
 <div style="position: relative; top: -5px; left: -10px">Powered by <a href="http://www.lucidimagination.com" style="color: #033268">Lucid Imagination</a>
@@ -144,6 +144,9 @@
 <a href="api/contrib-collation/index.html">Collation</a>
 </div>
 <div class="menuitem">
+<a href="api/contrib-fast-vector-highlighter/index.html">Fast Vector Highlighter</a>
+</div>
+<div class="menuitem">
 <a href="api/contrib-highlighter/index.html">Highlighter</a>
 </div>
 <div class="menuitem">
Index: docs/queryparsersyntax.html
===================================================================
--- docs/queryparsersyntax.html	(revision 792381)
+++ docs/queryparsersyntax.html	(working copy)
@@ -52,7 +52,7 @@
     +-->
 <div class="searchbox">
 <form action="http://search.lucidimagination.com/p:lucene" method="get" class="roundtopsmall">
-<input onFocus="getBlank (this, 'Search the site with google');" size="25" name="q" id="query" type="text" value="Search the site with google">&nbsp; 
+<input onFocus="getBlank (this, 'Search the site with Lucene');" size="25" name="q" id="query" type="text" value="Search the site with Lucene">&nbsp; 
                     <input name="Search" value="Search" type="submit">
 </form>
 <div style="position: relative; top: -5px; left: -10px">Powered by <a href="http://www.lucidimagination.com" style="color: #033268">Lucid Imagination</a>
@@ -144,6 +144,9 @@
 <a href="api/contrib-collation/index.html">Collation</a>
 </div>
 <div class="menuitem">
+<a href="api/contrib-fast-vector-highlighter/index.html">Fast Vector Highlighter</a>
+</div>
+<div class="menuitem">
 <a href="api/contrib-highlighter/index.html">Highlighter</a>
 </div>
 <div class="menuitem">
Index: docs/broken-links.xml
===================================================================
--- docs/broken-links.xml	(revision 792381)
+++ docs/broken-links.xml	(working copy)
@@ -1,14 +1,2 @@
 <broken-links>
-  <link message="/usr/local/apache-forrest-0.8/main/webapp/. (No such file or directory)" uri="skin/images/chapter.gif">
-    <referrer uri="skin/screen.css"/>
-  </link>
-  <link message="/usr/local/apache-forrest-0.8/main/webapp/. (No such file or directory)" uri="skin/images/page.gif">
-    <referrer uri="skin/screen.css"/>
-  </link>
-  <link message="/usr/local/apache-forrest-0.8/main/webapp/. (No such file or directory)" uri="skin/images/current.gif">
-    <referrer uri="skin/screen.css"/>
-  </link>
-  <link message="/Users/grantingersoll/projects/lucene/java/clean/src/site/src/documentation/content/xdocs/images.instruction_arrow.png (No such file or directory)" uri="images/instruction_arrow.png">
-    <referrer uri="skin/screen.css"/>
-  </link>
 </broken-links>
Index: docs/linkmap.html
===================================================================
--- docs/linkmap.html	(revision 792381)
+++ docs/linkmap.html	(working copy)
@@ -50,7 +50,7 @@
     +-->
 <div class="searchbox">
 <form action="http://search.lucidimagination.com/p:lucene" method="get" class="roundtopsmall">
-<input onFocus="getBlank (this, 'Search the site with google');" size="25" name="q" id="query" type="text" value="Search the site with google">&nbsp; 
+<input onFocus="getBlank (this, 'Search the site with Lucene');" size="25" name="q" id="query" type="text" value="Search the site with Lucene">&nbsp; 
                     <input name="Search" value="Search" type="submit">
 </form>
 <div style="position: relative; top: -5px; left: -10px">Powered by <a href="http://www.lucidimagination.com" style="color: #033268">Lucid Imagination</a>
@@ -142,6 +142,9 @@
 <a href="api/contrib-collation/index.html">Collation</a>
 </div>
 <div class="menuitem">
+<a href="api/contrib-fast-vector-highlighter/index.html">Fast Vector Highlighter</a>
+</div>
+<div class="menuitem">
 <a href="api/contrib-highlighter/index.html">Highlighter</a>
 </div>
 <div class="menuitem">
@@ -331,6 +334,12 @@
 		    
 <ul>
 <li>
+<a href="api/contrib-fast-vector-highlighter/index.html">Fast Vector Highlighter</a>&nbsp;&nbsp;___________________&nbsp;&nbsp;<em>javadoc-contrib-fast-vector-highlighter</em>
+</li>
+</ul>
+		    
+<ul>
+<li>
 <a href="api/contrib-highlighter/index.html">Highlighter</a>&nbsp;&nbsp;___________________&nbsp;&nbsp;<em>javadoc-contrib-highlighter</em>
 </li>
 </ul>
Index: docs/linkmap.pdf
===================================================================
--- docs/linkmap.pdf	(revision 792381)
+++ docs/linkmap.pdf	(working copy)
@@ -5,10 +5,10 @@
 /Producer (FOP 0.20.5) >>
 endobj
 5 0 obj
-<< /Length 1110 /Filter [ /ASCII85Decode /FlateDecode ]
+<< /Length 1101 /Filter [ /ASCII85Decode /FlateDecode ]
  >>
 stream
-GatUt>u03/'L:RQ/+NrVp&(BAOg:YiJL3kD`\`D!cp4$bikj?OT:Md;S^(=m+A2"!pDi)c`_I;mfr$V=@1$DL]UM4c0;$)EIYK&FISl?p5#*7D4GfAnki^?9+iSFXa)Rj!**j9/j!K*k4-nQ:Bq2Bq`oGOZP+n-,EV(bbSguRS&FRXUNl<a2OS63G\nH3lVU$6Ma4Y\s)"`e/fcSDF(`Rb6Gq+M)HJ65PA_"pG8H[McAt-t\,J`.V3S[aEdU[=3_3hnl=9NbM(l*Y07XMDb7AZ7dm(g@HJ+`p=5sWXm5S1Clp1&2;@'1e(!Z5+pF+jD+J$/D17U<Js(3L@l7OS:OMJ^_?cJP*"M!GP*eYbe%qho,o$Lo$N`CQUTP>Ue$qTL&dM8S[BHcc-CL#u$68o,^nqO$_sV0YVYKT,R[nN&,Bn79Lg&8m7FSE?6"lgs+XS<ahle%,jWp4P5eX2L%5'?e#'4u:@:dpHqV@'^^CAnLt3fr>GH1XkIMbZQ6]i\k$o!Be@[@SGMfgZaKM?8X.lc5J[AZL[CdBS).-RrOnLOGVpY)^uh0P/Qe"W+9ig;a(Y>kU?CQ4E4EB@MO?Woe]QTXmf9(gQhe2lT:917[2g.+h2tMJq.oW'E4cZ+afRY3pU?#WdMM%R`Af56M+"*@N9WQ&>,s@[]=LFla$H8H:ueG`a`mRVQT_cn[RQm3D8ONEPF-VT+oP*'!!d'@A^JKPN27oZ*Th3<NT4(=_(F34sI);1DXUQ>Z`,@Z.b)Fb"M+/b9C%'%_4Ft;q80$p`/t)_spX,B$FQT/KjEr8rGBgg^FI\-)/\r\h)Q?W(C?QH5jRL;7."Qe-6r7.[!jJV9E(C='O&d,$YcF9p&/;3^3!D50]HVX6GDL(Os<bVV6W@48r3bklEt:[5iApcOVmOj9J^5f%#gM30`bA5"h%u$g>1%?Z3:?m5`.]gR_W.WFrhuFE,#GB@#DoIeW2d?jpK[dEVsubb@QpGI+@'6GFjD)1Z+>9p";#1?0qe@LT'&74A6gm`DC"dp&U9f&QB%%;'9i@0jF6g0ed11cTd'EjL1"hoXKO0m_.1D7/Yp/ms(/9A\%ZfWEO:Xa7&^.khJ3+&,?U3<~>
+Gatn&?#Q2d'Sc)J/%BCKiqXP'CpI&FeS<eVm:Mn9$;Nr]+9a,upZlGo$(/q&F4[?"2X:0'[a24,ArFdC9B'@eTXS#uT`0frd!M-&&#p026J][2"G@$:@dSS.]1o+]pSQRRe=3f11k/\!S$$65h.OFq`Nji^(V(6jXNnL$hVq-f1CrHafs"u#%lgAEh-uXM>(YutBDq"lHYa"W7Ut+6bE_90EO"A26M>`*[,6c3Uus"M<g>5FZ.tWH*8$C-SV80p>4t%:fnmNhW2G>h#j?`Ef@sEg)Mu;apuh1n6'*/p@NGT&\1-*id11`<L7ef[BOFUJC'O^PLpq0-DNRVNg_GfF!l>M<7S+2a$3nFk#K,i1"mtnVPe0o-3)_Q[9N^2I3/ETW5n!f&fi>D;Rc1g&QWd!t>!+^3Vmo&6;)BD$rX?:hZHRa&Hp@rrj]eTrUij_f3:4S'^bmc;kHH>(WOW@0rB47f$]gEIAe0Br/tC%2e<'@rpLHiI4@Z3sdlM-P/CSri\,^IiRhjhe[G"YF9_>;M+h-UL736$D^I@CW"Ib:u^bco?:8?*GKTu\S4YLj?4HCX-MB=XsEB5.CZX]<2YHeW_-#:R4q<jI:Q.A$[/OpN4o[,[K.>!(kC-)7rZ*kLE,g_u51Qf_YF]:ObK+an;A]*PaZSNId<Pu8AcFh6(2k@EWSS6gAVTFoWmT`4`EJU]qEu@u`]./4BYq4$un0#TTWkJZY6L\L]YfbJ!']kgM6`l-;3um9Yd7ngBe9'78*#gW=L\q*^B.%0fT6R=un3?$"<*N0;8WgXh=C9tpT-8/g(l],;Hi0pkE0`P[_KKheM:g,mLG;83O8Bl=I[@8+RpK3(%1J"8jLV`9TgSpi*Z[1R'ap;h\L=oS#;K4[5>4eH,?X%aNlm29XhJU?R0n./Up#?H_iXD6D_Zu<WAU0ZM?i[p4Z:ieMjsQ1aKdP!2*NQ<cZMN[=-j3h^h4^j5p3te.ttI&I$r3#+g8lq6!MD8fVbfPa!8ShaI*hW_#22q?>Nq@]t8A$qAj1HCLIKoYHVC,b5.#j'_g@k`DXE<AZt:&f]GbR\:EGNe^[1EW4f6<>?%Wl&ddcVrWf_4&fC~>
 endstream
 endobj
 6 0 obj
@@ -20,10 +20,10 @@
 >>
 endobj
 7 0 obj
-<< /Length 913 /Filter [ /ASCII85Decode /FlateDecode ]
+<< /Length 971 /Filter [ /ASCII85Decode /FlateDecode ]
  >>
 stream
-GatUr?#SFN'Sc)P'u"K;iqXhuaCV?jUoBPYJ^d9W1n!8e7S%2HV>^.05(FL27S;Y))"PD%STB2(GW,CR@/pss^ap?HYT.)%lk]aj6bCDWLE)"U[K4-]SdAi7+Ma?fqpWf3X%]NK]>@CXnFud#$iY\0GAG&k-:`\"@n8%m/cM_N[4(#W/ojmfo3ce7BP#h]9Zp!2%)&/lbMneIf=Dq19-Y9FfB/O0ldH)t,pY\4I9@?_HncJ(&BK.\H<ejh9R964'LCe)4f%mo0IDsE$fW(0@5\UDN$<l%_t]J!e#crPCA;cU(e)A#\`>lC?2@Y`ns7d(;(cm+L6kl)V:)c],!C8EEJE+M;SRZCfSDB#j'J)@XkZL"<LSkg'(DeKGFp9K,atit%s%M`P6ETS9sAKZ]R.T0#"8IG*pYppD,e(&%:uE,29]?mGt5;u/sQ@:^2LHm2eeBb#8D=^'A"XUQ2;DP5FX$EO+YP&\eIri90WmWkNjJ=oJpV57Cc:X'<bHh="7Ln`&\%C;7I.%&oRmIVN#:Bes:Jo<,)!_L-UI"2]-a%c/'/ra1\l,`RJ*;&,TJlSR\4jHnd;2#(oMX.H[dE<dOt]UN@#!NMs=$g8jL,qjn`a*7e.$-)#,,>X,i$\.;Smroko@DeF<9V'37q'lbgK8'O4M`k5k^[F<H%[A/2Ljk;'fO>+>;A0dj6fR1I[C*&e?VTq/04+cD/-=N@H2eqZ[c."jss&C=>=C%R\58EJ8g$#9(J"m>_gMS7'XmRZ(O>'LGOGk5Nj(_uAQ^H"V;+X%f67JX=]3DlGUpEdSg4A#H:5#])G%5kY&SF@`G/LhP\hCM\A\)-7>#/2^r_+.m7g'uaNb0ei%dpbTm`*^"0aC^%<[Q?2W2eSnFrni4F&75P3jH-,oi3['q<\(__`%Jl~>
+GatUs?#SFN'Sc)P'u"K;nGElJjK_`pdaUkS5jG.fRS5Ds&JAhPC&e.%%j2(%O]&\ML:ltbk8YRj1`^r^1Ood"@(I0tYT.&$g_U&Z,<*[]LE&`j\cKQ`SdAi7@#-O)m\cNrH;dr5"rOB"5M)N3n/G'93Y-]139j#$5t["&n5m<'H'&N!,.Ani%8C?[W3@e;L6;l\"XkaDEL<^Vlci#"j5dqp-0O],dRB$>,U(U<ZDm+"MK'*Z[^ZB%H?=,33//j&WO"-Yl"$njDcdpq=Sar-XDOR^?kGaD!o=kpm.5N&'HiM/aS33=4dgDSEYL((aW\oq]ePY&>S,fN$F=p>@Z591TUsSS]%-COoj:Gpj-]TgQ^ii;9,FXU4/%g`.a1$,[XUl6=XniX/(11_(FPrnP5>"A_*t.0*r"C$2f^8P<bb+0Q%7bWfmTLnOf5/>8R>0ZD3GE98t+_I0al8=o*S<IF*j_SA@59H^$Ej83NJS&0!gj[(EElromK!b]hOEt5G2rtR.s.-YM@)f_q@/dHgFHrdA?V)]l_`]E6aKm9V7Ml>aUMV+*b31)+i_)i>dT1UMpGfNG5TtI*pNr)f%f&Z-$2)]-0uoh%%GQ:V4)iq1!="84=4DP)"I[o@C3B<'HohDnSF_LD4ge193Nl];'Ha/n;_tBst#Tm<`_NIooc8PXN5o^&@:9i@\*P_l'QR]q,/IPt&,AY'LkqULk;Y(;VaUgZcbYg/C48Em57#8/PX(A0dj6\2(rIXrj(-eL@'qf+A-5/d^!S]hr!PCoV*8s0#njL#iN=o2D?3Sdo),qa=[5@b5H"F8KDa+@m>')Cu)RK2Hmp_3^P5iDE04*'l%5A%PtArhMDGN1(-5S"5@;lPbC]1<#aa)7^(6>3`iS^705TZB_"4fDV*kI#\J!r#AF4qAe:-<RQmT`PN[\BoS(p;kPUaHe<YG\puV'jh5HKg@a#u4br4.qZgfZ;F(~>
 endstream
 endobj
 8 0 obj
@@ -87,19 +87,19 @@
 xref
 0 14
 0000000000 65535 f 
-0000003046 00000 n 
-0000003110 00000 n 
-0000003160 00000 n 
+0000003095 00000 n 
+0000003159 00000 n 
+0000003209 00000 n 
 0000000015 00000 n 
 0000000071 00000 n 
-0000001273 00000 n 
-0000001379 00000 n 
-0000002383 00000 n 
-0000002489 00000 n 
-0000002601 00000 n 
-0000002711 00000 n 
-0000002822 00000 n 
-0000002930 00000 n 
+0000001264 00000 n 
+0000001370 00000 n 
+0000002432 00000 n 
+0000002538 00000 n 
+0000002650 00000 n 
+0000002760 00000 n 
+0000002871 00000 n 
+0000002979 00000 n 
 trailer
 <<
 /Size 14
@@ -107,5 +107,5 @@
 /Info 4 0 R
 >>
 startxref
-3282
+3331
 %%EOF
Index: docs/contributions.html
===================================================================
--- docs/contributions.html	(revision 792381)
+++ docs/contributions.html	(working copy)
@@ -52,7 +52,7 @@
     +-->
 <div class="searchbox">
 <form action="http://search.lucidimagination.com/p:lucene" method="get" class="roundtopsmall">
-<input onFocus="getBlank (this, 'Search the site with google');" size="25" name="q" id="query" type="text" value="Search the site with google">&nbsp; 
+<input onFocus="getBlank (this, 'Search the site with Lucene');" size="25" name="q" id="query" type="text" value="Search the site with Lucene">&nbsp; 
                     <input name="Search" value="Search" type="submit">
 </form>
 <div style="position: relative; top: -5px; left: -10px">Powered by <a href="http://www.lucidimagination.com" style="color: #033268">Lucid Imagination</a>
@@ -144,6 +144,9 @@
 <a href="api/contrib-collation/index.html">Collation</a>
 </div>
 <div class="menuitem">
+<a href="api/contrib-fast-vector-highlighter/index.html">Fast Vector Highlighter</a>
+</div>
+<div class="menuitem">
 <a href="api/contrib-highlighter/index.html">Highlighter</a>
 </div>
 <div class="menuitem">
Index: docs/lucene-sandbox/index.pdf
===================================================================
--- docs/lucene-sandbox/index.pdf	(revision 792381)
+++ docs/lucene-sandbox/index.pdf	(working copy)
@@ -5,10 +5,10 @@
 /Producer (FOP 0.20.5) >>
 endobj
 5 0 obj
-<< /Length 857 /Filter [ /ASCII85Decode /FlateDecode ]
+<< /Length 906 /Filter [ /ASCII85Decode /FlateDecode ]
  >>
 stream
-Gaua>;/_pX&:i[6'n/iQ`O-.5n<d8BenWo<"`4e)*aEYf1pBnGg1A%52D!MJZB@aO7MLt@`uX(5mW+h/_<FAo8-BOG('1_=4@%g_^]9c5#,tm<&V;0!5ZWN@PM),^GN&a!8Xu/`P7O^!85GiVrR.4Zi^Wl/!T-1dANXIQ8alJC4.9Q5PGi^"W@]'%iLO_LWXuHfo>+$-E4!js_iTSR\!$9NWd>:l029s5rQ^[ENPT527fc**m9sNIPD1ecj5(jN[9kWGmWMsl`M%K9`1`CQYn-FK8H_D-J-gLdgBO5.'ZM)0paS91+ap4PQg4*q=-^h_MRjS7E@VEnSZiFNi?DcZ@G93!L6"$a"$RUCp[i*]=6j4Jp5L<@e.3_G(Q0)@eTO5=A4@D[BtF2U7nF!(!B_>*p[r1)rEn6kKc3mq#<&=g&(o8h[\s-ol/JIO:ZlMmjLRPEIVo0\clShlF8ZNqEDTHgP\F./oUYta^`3)Se[9+8e#N%A029dC$_ICY9",:L^Xj'bK]=3uM:^J6GR-G19^(12]e=<m.e>N"a%q)poAk*k@ln\"A_5DjEGeSPMEgSWfB';\o[bUefD_AbVU_`[<JM&RQh*RaO+W3:nTn@o$L[6K!</BfA\en<]g4%9*p@V)osJl]h:&jdmJU1%EPE,ApV:W#*3\+>k\LcO9"duTdAuVjAbU)P1qCiT^r]eX>foAQIr$pN&V8.IM8Ob2"o$iOhG(U!`@s3(g2.(HKE<a@,.J-.m"eGl^`]#+3258_O"DlW+:X)=5?H%9[a!#*hVSUX:*Kru-Y<;C^rt>^\QZA$>^It;C*cT:PY+:2[XL^4L!I.dokVq\r?)I2d%:<C!N)L~>
+Gaua>hbVu\&BE]*=89$kX0J3T599j-)edrY)R5'7%j>p9>%/9qKlql.G-]*m@ni'9P,`9aSF?#/gY_d$PKE@M6d3RR--HV"5M:^_RgDO%Hmjkh?f2Ua"VF?VoPJo[9!_`AKOucobe<8=FXAAIci/?.OimT*rAm80:i]tg;aab)q+3Oe;+r4L$hpJHC?OQ.W`LYdZ73*998KPHP*^^Y^^E1V8U=[N&&R_9WEQn5R?EPL=SMeSU?&:<9'g&.0>qk_q4l=5)$=`26Yi$D/nCT0[Y56?R0SRrm+Db@fhQFSFA?<cDRo:4Eopg0`-rl[_f].KJ@uOp-$8O#Q:ed/2HBXtN/9uK7m@KU:s=962N/15:LmXHIN.!bN\F<pKe/g<cASuBPHrLL@D^"Rpj)TY%spIL2[Bo\:Ls&tX8<'8b1S;L"-.)Vm13\/ql`WTT1*h6fLR;IeXksq"ji(<!n7aNSmaek$LH"q/IT%8B7FjXD>R",Q'&[]=hrm8UUb]=B)d.SgAnUMCEH9ai=rL%86[d<!aqW+>reR=`Q`76^'<om$fB5lqkIlA-uRN$1bB^0#57YqDB/D9a;V#7Dif`tK6uW+h2</t[:g*mg0^ZMBq=Kt"]B<M(4$'!]W<'cmaADm%($tf=1/_!Tb!A>*X2lC,J^M2ZH1Mn@rKatp^2,gJ_!](P<H5<@";P3i)Qnuh-.l.OBX.nPh+Tf,fYDG_GTCV*"#GZ]8pk^3>c^QQ?$^MgM%o'F.%s(+'B&UcWXLNIn[=ij7g[VXt<T+P<L;TM2b'6P<gn_8.2ep>Y\%2H,KKs&0.t%kFfD[G*W5j@]u--1UYqM.q#ODps(jA/Oi:h=pZa6SsMnS[;6m^%=7/O/^5=cPf-s3G<YF5D/<&=BMf.c066F+*s'7$g)^~>
 endstream
 endobj
 6 0 obj
@@ -33,6 +33,7 @@
 24 0 R
 26 0 R
 28 0 R
+30 0 R
 ]
 endobj
 8 0 obj
@@ -108,7 +109,7 @@
 22 0 obj
 << /Type /Annot
 /Subtype /Link
-/Rect [ 108.0 418.766 264.14 406.766 ]
+/Rect [ 108.0 418.766 237.488 406.766 ]
 /C [ 0 0 0 ]
 /Border [ 0 0 0 ]
 /A 23 0 R
@@ -118,7 +119,7 @@
 24 0 obj
 << /Type /Annot
 /Subtype /Link
-/Rect [ 108.0 400.566 252.8 388.566 ]
+/Rect [ 108.0 400.566 264.14 388.566 ]
 /C [ 0 0 0 ]
 /Border [ 0 0 0 ]
 /A 25 0 R
@@ -128,7 +129,7 @@
 26 0 obj
 << /Type /Annot
 /Subtype /Link
-/Rect [ 108.0 382.366 234.812 370.366 ]
+/Rect [ 108.0 382.366 252.8 370.366 ]
 /C [ 0 0 0 ]
 /Border [ 0 0 0 ]
 /A 27 0 R
@@ -138,7 +139,7 @@
 28 0 obj
 << /Type /Annot
 /Subtype /Link
-/Rect [ 108.0 364.166 211.816 352.166 ]
+/Rect [ 108.0 364.166 239.812 352.166 ]
 /C [ 0 0 0 ]
 /Border [ 0 0 0 ]
 /A 29 0 R
@@ -146,166 +147,184 @@
 >>
 endobj
 30 0 obj
+<< /Type /Annot
+/Subtype /Link
+/Rect [ 108.0 345.966 211.816 333.966 ]
+/C [ 0 0 0 ]
+/Border [ 0 0 0 ]
+/A 31 0 R
+/H /I
+>>
+endobj
+32 0 obj
 << /Length 1870 /Filter [ /ASCII85Decode /FlateDecode ]
  >>
 stream
 Gau0D9on$e&A@sBka2?U>,8lZ98[Q>TIJ<lMkXm3TI&<(<Yc]RZK(r\Nb$6Qb1*TKUf_$Vd`S(=O)Nk`om/?>Z^?00q'=3_5OdCA+f4S`aNmc1dq`muT>/&3AslGB09/Bo*CfGZ3uS\LKUrt9Eq]+kojTho:H[>pYZT.tD`r^fi.lWVYD*'?*d`gIpW!Y6?@$gs>NWnc9eIo8\UNI:YuI?&H@L3uG:1nI>O63f+UCXX`rh2Q$V=$["XERd42J-E0M94CKO>dBVq";"f9[Gh2!T+HX/b<f1?eb)NsDL'c&UuHRqK&o:Xg>D_F_,Ag5-/"V_G(?3^h#PSZ&.negHoem8hfGcj%JO.,$uF7CbM!PrKn6og\/gAKgcA"O,Sm?!V8#\W,GpK5RV&k/"J$/m^4F)eBS-H`j2[N/NTBMhp83;/`6_&1k!MUV4]O/t6%Eas*l9g_c!sHgrY0]+U@e?D,jt]P:80($8^9_HIo=fkCJ$#`Ht[g,Gk!Unj6N4Xm\ZbJlfm-t$V[`#Fpk*,M_I6CK]a`#1O08_aRM(&5[\iIt?:9Q2=BK@8g]</@Z/X>c>f[l[U+djWWefE,^`ZlkJNQ?]$<Za*MhU9iUa:`La`]1Q5<T*o-odj<G_&u"$2(U\];$3[GqZcno%C](S;AeGhrhIpcrBPLi]pV)4C%Z^+lC=!k=Xj?SSpB6\D5!Rg&,qC3UKo;\58-^%entDp>FhRZq4u`@rE:DeF+A/j#h-+l;<qEU)49*CrHsZ-9HCX_P4t!kZHspNDHqN%f.CEG8$HB2&E:fq[UP'uYck^V2I&PP'BfZ/[/[elaBY+%`2MCD9)5[FC1`.nn0hH<JRUM_KPkJoH2V=+pRO6hBK3MK>Lq?^dZGbk5LEp&i]qlrYZ!_HX_5ZEJ"S]I&,+dAW[H]Q8E:7b$AdJd!d'%`o\CR[Z/-gcO>@(N'N'@h=;8jV81.T>3h$p=De[j^T0p1-HSIU-S*h>lRcIn</qpp3-h@E5reF>DS/:^SN]En<++q)h\"=tIKN1hGB<UoYT[lk``e>&u'kiTQ0l].ful3a2UK$.-989ddI"Yh6)*1i7?2i%_qIks?*/3'DIDY\Q_)GA".[2Xd`,Ql+@.%>9-:Y3imOU?2dlp'-B5hj,2%8$pXV:<t`mft+jNmu+%c6(rLq[@pDJH)(j=-`0Q[`qP]H=Omn.45n1\Ts]+I,nHrj**.;DVMHH"DZZAe;?s?cNP)G'sn#eC;fHL=3%IZ%C.6&_*F:G1kq`]c[<>T&`iBsmsh^a,U;pq'u1trp>&%N7qYQ#-mmHSFoSlslMnU;=9.3"Olu,n^fV!LT+0lnU.tMZl/F'fcQA14WfGhJ@q68`UJZ7W4&Yj%95XHCb`>X,_.,q-82-$WFTSKM*(qVj%A\(jX#62;??/0PNgaf4UPTYV<i'!SMT84+[2k+_>$_o__Ka0$.RB/_QkJnPD^n@n-Hbujj=Unamf5YO%\9KSo;V?\Lq.(AbMm5[EA.AZ#mUZ(VW4=g%*`B+!JSu1Wl7bncpua_C%4#+8phkgUKY"n#U2I<G53A@ft$2l7U+p2,`\O^H;5HJ::^kX!a?&u'RPFI/FnfBVuP/K23As&Hg@NDZ]ja>a20@3_kHK17[%WRRHTE,cr+&N-[<n&I/G1g95]4)4:iL@U!QS,P(d3Dejrq^K!2aAB"J%"Ub%??cJX*9m^WQB00.cGVC--WKHsF`QTf51CR;*<0'O3b"Rq""n6e4NP9/h@Ib[0.@jk(:TBm?i"A?T;]r7b]\7BU?_73/V*JW"u;9SX0UUr>N(Or`H?ET[aa-Wj`#>Q(kXR>pNXm/:RT'(4Y^-)Vh^>GKrFuics0"T[>?&S-Y>ClY25NUXA<W~>
 endstream
 endobj
-31 0 obj
+33 0 obj
 << /Type /Page
 /Parent 1 0 R
 /MediaBox [ 0 0 612 792 ]
 /Resources 3 0 R
-/Contents 30 0 R
+/Contents 32 0 R
 >>
 endobj
-32 0 obj
-<< /Length 1798 /Filter [ /ASCII85Decode /FlateDecode ]
+34 0 obj
+<< /Length 1750 /Filter [ /ASCII85Decode /FlateDecode ]
  >>
 stream
-GauHL>EbO7'Roe[i%=1eXWTl6*]Idk9r?1rib9Lg50+l$RL"`m=NCC54W%:]*([_Zdb40E&g3agG<UG1Vjl6.c0F>HH@,G"=Cj\d]f;&2J&JqUCDQ^kVQ!W)DnC>-HM[:>(l?Yr:MuRj"1YSgXo.cX55\9]IujgZp*4KSH@J&+Y4kQB_[kTb33VsVEP6;d?;*.F;\sTq2@&)4UYP'f99$+/P;`8:g)m5l6L:M6NAMR"Ea;@k4'kS/U1J)5q"]c!XC1lXDtE82NA)U)kf-X[7j7M;<hKcH=>IJu9h@%AZT/;-6=m679RWAlG,E9Q5^35<@<sM%BM)418rS+g'BkW:9E!6'^*?7^Drt]3kLFotq;T"&?Cu8(V'HsX:*\c)cq/QCdBHEXiuq&o'%3HIO:k#0)h2RC-)]_U\KWkNn!PUtaOjZf4Sr#8U>GEF?nWOfZIC71P`/X5!I-:/<7b_IGK/i?<\@).<Ma5TL'jAKarD"\Ub9S5PSpW8eZ+O_G-rb8Egn<:B0jH[7Zpnq*sSGIkITrYjgOObrMDge(s/YN49/!<_.)('V&2L4,AIq,TB6'FhuiINL5)=B(62FK&P9-V7[-=e!dDpJgn5!-+mi%@:BRpj5ul]D@2I@3(_)gXNtKk%#A45\.ij<%SU$bJ0Ec3m-MD_*9,AR@@Zi(;"/7(>*(9b\@!+\InD-k+a3!cRV'r3oB)dIJ+pJSSnHbro60T-!j#4)PKeiG3'tApg_9('RD;LXkHppn'BLK$`,@/3=N$K#$%@].Ba_f;-0o\K@Dl_%1pk2UEk[n[CJa3/CkKS>-8lYkkOR$h"EsHpC0.c>&PtsVK.r#J`Q^M+qBh[1CU6VQS]Uj?1Ke[p#*[,lJME@t^J^3]XB=uR]@[JlXJ+fL[nY(cK?Vr:]^Eq[QKL+\__r2ZVN'qXN!;H+oE;tnJ=n"(q4_+kUdW@;s:.]a@fY[tWaF`jkFtE=3^@XOp,W?&TXFB6_nGuo"WX#bm*=?FE(O#TnI-NVV\=O\nfa-tY>D)HIRj0CV?<cst;_G)qE[5.U#DpQof>pXlCTo3=jDH>d3"+km4I9kaj)`Bu[XrQ.XfZ5aV[82Mhd(Gf>ocr'U\c2$(G^FnPun;)S@IQRnj_PsE.:^7Aue<[9+F6'_a,@A+67t;kIXSY!69:G<T*Q"&:i?F56j!/QgPug7^oc*0^B^h[HU-$![Rt#-#q!qK@6q%YFpOKZtp:c1MJ;7c%U`Z;)k[]`UaU8a!d:meS!=R"\W9oP2Aj/&l-K"^r7Ua'b6%>iG!GGfQLT%F^*'41PEQ&+fIIO2*IWf.hn`,J^>.)jc>Et(HTt)H+Saidb"b.l*;]ukqC0LgtjU5F)G08=Cgag^og\D&V.#D*@'Jl%aEPgDat1B:uI&N,tFr]!\4LL-$tsbc)g)RCE)'*_5K\D5RWTtbV#llX_8V(Q:AhW'*fUYj#n#[+n+2W;s6hcX6-Hn[SU!lZ$]3%U2bi(iHgBU/-=LgN$Li?KXR5?]=;ai;kSBg2qeXm6mIeaBeXEH:_I##82LGiT+2;*mcN-oQ<T<BMsfH`'VRN,5T#M5`p/T=)'l__hLQogJGjt\[HM/`C$Om`YBr'HBm9]aUqqS<)kb<H91qgA_u[%$ql08?DKl\T?u)8LGAm9Q5X@c.N7mkq^q/at`otiR)kiZpPJP0r",l&T(*X!_hkpZT'm/ijoN%)@B9gH[K*n9fn.+NIWD<3+fls*=#)l<P_"Zl'jE/!^;WmRsOAX%a=7nAYVU\4(%Zg;=s3NKEHWr<B+F3BCJZ@rVF(QQ,~>
+Gb!;d9on$e&A@sBk`up]),]9P-:+qS:_ZLQ8.TPJZp`Y:-rQ`a479<@aHEWu=5);$(.GLtg8%,W*p:f.$t$+PDcVeB$gMQeF14\Gi]9pN_>bWoIkO1H,:Q>,_k$J[_rB4u#D=?hDngN93n/ceX3JLE%<CEg3o.Tu,EWp)X2=\mq&?`,U>#S(/A9M+cQuNEL2Hb=F3l8;`b)nop"7EoWgQ]IH$*(i*K04'Yq+uVTgUK>_S&Y)@q3iDcjcG`E61j"G+sZX^RWid$Q+=bJcWl[f*BUO""R1QY:D@g%o%j=bd6#CCdP*O;/:4mq_m0dX[$h)%bpV+7pApU]TISDoAi0',\c97:AZ&.g4"fpRQ->\3qEl3]5p$"LM9),_<!KrDWk;"NudM0::Cdh8O@giFU0E&a_9]f9d[j3>Zsq56L\=IU&^6+V80c/X.#Leb;_t*d54O%iWo!]Fl3:PBqj<>"2!$D:3bm;A6,!oA1+CM:+>i:O\)LAImKZ78sG=J(pl$/R.5$J^e^+A$pkS&RX6!10#4mBA]2/o%K3Ftnj8M"::YpJ1ka!!JP$NKdY]!NI6WSU:;R8"0i&6;DClj0n,j;16]E2t8-!U(2S;Ei?@^e4J0,_VK/Q`8WoDQf4*H886>\Gel>1,+!3j=L(g`VuCkjH920)Zg(N8OHYW*guS6.%oPD:FKqKf3S7ZH<6p3u4Q.OkT6,rGQ:WYC+?d#A5Z`J@-)=bV7tO_6QFco';[!h[hihFoLS-#jLie?T2\<`:+XHiCcl)"^]Zr+:Zm%dI5mKDkuPq.W6KdF6_=cVGbE_lGs#'#_P/CsT-*L_BOX6c60^^P9t.2GFF(L4h`5pQOZq*6gAm$:pWZa7YZG;QL>F`mbo(UFAiQE/#\C=7rj33MPs)(<pmU#akeX&G5]dn);OVOU3TpZgh[,EJ*LjB*L7^M#DRK=G]@8Au=PBBt]f0)7&F:^1SqU.!N<g7Dn2sm/&CALP<J$>eZI8k8B!7eT[iOF\+]9l3=i30Q8.]H5kO%1+@8;(tH=L,I&=#Ajm"tY+`ECVo8>YVl)R.1eq.6'q.d=F3Q6.bd-7BH45W7:o.hcplKM;A?L$ie1N@m1g*7Cc';:3C!8c9%k&usWVDr9))#4;;%[s'$%*:<K?Vi7)OEk&fPEq[kJ["MVgp?.51;&m9/tDIghIH`Pa:Qah1gRKC4u^:N]U*$ipiQlq910O^;ou"eJp5qX4,M\[H,<&1jD2tcsZ1g8cssC-PH]F6<K9\GeGeU/f]>V=W0P4lr4@hAVL;Z<JHU;\f>9N'kllr,k90[i*N;g'M?@fM'b>`LaqF_1<$B-Js'];X=FZ5Q[:m:b\Y#Sg5>`dY_(hYfPqm<>ZTY>0oeCCNu,u7VfM/;&'7D<R!?LaQo:9L7jons3H*A7-qht[#7K!L)QC'ERA4erES`:3/YqU/^XUG*I6AOtR;O8_P-lkFRmp0@5R7c>G/-j5'U-SPX]5*_"YUu7%(S]ES2t%8g"Ko6-]r%9<T@irBU-XKD:%?A<-)d&Rb'FBQA1'A@NPZ-RPPU0o6ekuDhK_F.#(!b?8$.K+ZfJT4XOk_H.[Q.Y3"8A\;.t0XhRtq"gQq;#_dXg8<6E:^jmij$rKbID1j)sM0fonpVD2_bXVZ4gIVNaK@e[dj)$,sN&lqb-OBciJ+rM;QcF0[FHsn=?:`,&kIT'nZ>(Mh;lB=P*2&)d7.':]h'eLMoY\7ELVAd`f<.r>_dU%;3Kj5g!$7gsU]~>
 endstream
 endobj
-33 0 obj
+35 0 obj
 << /Type /Page
 /Parent 1 0 R
 /MediaBox [ 0 0 612 792 ]
 /Resources 3 0 R
-/Contents 32 0 R
+/Contents 34 0 R
 >>
 endobj
-34 0 obj
-<< /Length 398 /Filter [ /ASCII85Decode /FlateDecode ]
+36 0 obj
+<< /Length 608 /Filter [ /ASCII85Decode /FlateDecode ]
  >>
 stream
-Gat%]9i&Y\%#46H'g?MjR3]sGHXqpU+V(1<"rsp+h("CrU`F:OT(HN-`W`'7<F=AOCHks4j?=f\KY]Q/%j[uB5q*Q*6FPc-cB*[fYU2#6n_`]&V$+fUO/sJ2/#(uVkR/\,70R3Dn2e[7FrJN8/e`.6XtC[VcXX%qIM??n2l:=^e(?dCk4XI,G_bW?=E@#0[*BIh5nhbI!6sZIGhO+o*mCMUef%Jh3)#p()d?U'Aq),.9U2g;Be]!([0q2:19+3p!>.aQ>=-;_&GnVf%-,Tr3MZ]i4\YZf[b#sP>*I?b0IC$4)0'7d@]C\!+tUt7<d3tNp>pJLFT%@<g`)Sr<1HiDWo7E5&]*c)aq`11nPNi]N`BtcdT%-LmJ!C>]^&,CgA3Ilj2gWnXa6='~>
+Gat$t9lHLT(r#SlHqY&rj4Dr9*8>>03c3<rEpImf^`c7eA=.3!^Rt<7VG^DgLl]U/4C@k9M`t_"Y6[_achf,e5Y7+arlR\0]M2m,6qgcO%ak%7mbR[IJH`B(HU3bh(YLO;_]n@V,i_5-+JG<9BC`^W:?KPL+25$pc)#D[f<d3SfWf-,E3<G`l<7E@>hq0*f@i7Fi.h.Tg#l3_"dLPUPQ&i>qn&N#,,?g4gFjZ;alG'dTOrDhJZdGRm3TSnoXFP6(eA242ZuNN<'FF9C"lY!1W=tubhl7b3a,<-\X_nE"9(d"CN>:D$QR81Dng0#g6lC^:lV8,lqY,rOKnXYRip5P2*V7%B\/ZF$"n@LT.4'J^1S8Cq*.o^ann!Z@TI5tU4BT(s8*8*+N[^YAua[Y&`mL11?)8a9KA6L3FqAFI!a9:a=S^611=_bjdI.#*nn=eiCVe`d&\/Qs80V\;@-5pAPup1n+&G^M6CIt0/$?\8NA1ZWViX:2'3?F,+UV1-mlJ1[MoPSBd/AmhXHd%LZ\0U<on%o?E-3!+&(UE(o>?+VIE"+KJh\ck1KX[D(r86Y-u\_eQ<*Ceb&tJDAQ^@9`*QY_<1\$^ul=]~>
 endstream
 endobj
-35 0 obj
+37 0 obj
 << /Type /Page
 /Parent 1 0 R
 /MediaBox [ 0 0 612 792 ]
 /Resources 3 0 R
-/Contents 34 0 R
+/Contents 36 0 R
 >>
 endobj
-37 0 obj
+39 0 obj
 <<
  /Title (\376\377\0\61\0\40\0\114\0\165\0\143\0\145\0\156\0\145\0\40\0\123\0\141\0\156\0\144\0\142\0\157\0\170)
- /Parent 36 0 R
- /First 38 0 R
- /Last 47 0 R
- /Count -10
+ /Parent 38 0 R
+ /First 40 0 R
+ /Last 50 0 R
+ /Count -11
  /A 9 0 R
 >> endobj
-38 0 obj
+40 0 obj
 <<
  /Title (\376\377\0\61\0\56\0\61\0\40\0\123\0\156\0\157\0\167\0\142\0\141\0\154\0\154\0\40\0\123\0\164\0\145\0\155\0\155\0\145\0\162\0\163\0\40\0\146\0\157\0\162\0\40\0\114\0\165\0\143\0\145\0\156\0\145)
- /Parent 37 0 R
- /Next 39 0 R
+ /Parent 39 0 R
+ /Next 41 0 R
  /A 11 0 R
 >> endobj
-39 0 obj
+41 0 obj
 <<
  /Title (\376\377\0\61\0\56\0\62\0\40\0\101\0\156\0\141\0\154\0\171\0\172\0\145\0\162\0\163\0\54\0\40\0\124\0\157\0\153\0\145\0\156\0\151\0\172\0\145\0\162\0\163\0\54\0\40\0\106\0\151\0\154\0\164\0\145\0\162\0\163)
- /Parent 37 0 R
- /Prev 38 0 R
- /Next 40 0 R
- /A 13 0 R
->> endobj
-40 0 obj
-<<
- /Title (\376\377\0\61\0\56\0\63\0\40\0\101\0\156\0\164)
- /Parent 37 0 R
- /Prev 39 0 R
- /Next 41 0 R
- /A 15 0 R
->> endobj
-41 0 obj
-<<
- /Title (\376\377\0\61\0\56\0\64\0\40\0\127\0\157\0\162\0\144\0\116\0\145\0\164\0\57\0\123\0\171\0\156\0\157\0\156\0\171\0\155\0\163)
- /Parent 37 0 R
+ /Parent 39 0 R
  /Prev 40 0 R
  /Next 42 0 R
- /A 17 0 R
+ /A 13 0 R
 >> endobj
 42 0 obj
 <<
- /Title (\376\377\0\61\0\56\0\65\0\40\0\114\0\165\0\143\0\154\0\151\0\40\0\55\0\40\0\114\0\165\0\143\0\145\0\156\0\145\0\40\0\103\0\157\0\155\0\155\0\141\0\156\0\144\0\55\0\154\0\151\0\156\0\145\0\40\0\111\0\156\0\164\0\145\0\162\0\146\0\141\0\143\0\145)
- /Parent 37 0 R
+ /Title (\376\377\0\61\0\56\0\63\0\40\0\101\0\156\0\164)
+ /Parent 39 0 R
  /Prev 41 0 R
  /Next 43 0 R
- /A 19 0 R
+ /A 15 0 R
 >> endobj
 43 0 obj
 <<
- /Title (\376\377\0\61\0\56\0\66\0\40\0\124\0\145\0\162\0\155\0\40\0\110\0\151\0\147\0\150\0\154\0\151\0\147\0\150\0\164\0\145\0\162)
- /Parent 37 0 R
+ /Title (\376\377\0\61\0\56\0\64\0\40\0\127\0\157\0\162\0\144\0\116\0\145\0\164\0\57\0\123\0\171\0\156\0\157\0\156\0\171\0\155\0\163)
+ /Parent 39 0 R
  /Prev 42 0 R
  /Next 44 0 R
- /A 21 0 R
+ /A 17 0 R
 >> endobj
 44 0 obj
 <<
- /Title (\376\377\0\61\0\56\0\67\0\40\0\112\0\141\0\166\0\141\0\163\0\143\0\162\0\151\0\160\0\164\0\40\0\121\0\165\0\145\0\162\0\171\0\40\0\103\0\157\0\156\0\163\0\164\0\162\0\165\0\143\0\164\0\157\0\162)
- /Parent 37 0 R
+ /Title (\376\377\0\61\0\56\0\65\0\40\0\114\0\165\0\143\0\154\0\151\0\40\0\55\0\40\0\114\0\165\0\143\0\145\0\156\0\145\0\40\0\103\0\157\0\155\0\155\0\141\0\156\0\144\0\55\0\154\0\151\0\156\0\145\0\40\0\111\0\156\0\164\0\145\0\162\0\146\0\141\0\143\0\145)
+ /Parent 39 0 R
  /Prev 43 0 R
  /Next 45 0 R
- /A 23 0 R
+ /A 19 0 R
 >> endobj
 45 0 obj
 <<
- /Title (\376\377\0\61\0\56\0\70\0\40\0\112\0\141\0\166\0\141\0\163\0\143\0\162\0\151\0\160\0\164\0\40\0\121\0\165\0\145\0\162\0\171\0\40\0\126\0\141\0\154\0\151\0\144\0\141\0\164\0\157\0\162)
- /Parent 37 0 R
+ /Title (\376\377\0\61\0\56\0\66\0\40\0\124\0\145\0\162\0\155\0\40\0\110\0\151\0\147\0\150\0\154\0\151\0\147\0\150\0\164\0\145\0\162)
+ /Parent 39 0 R
  /Prev 44 0 R
  /Next 46 0 R
- /A 25 0 R
+ /A 21 0 R
 >> endobj
 46 0 obj
 <<
- /Title (\376\377\0\61\0\56\0\71\0\40\0\110\0\151\0\147\0\150\0\40\0\106\0\162\0\145\0\161\0\165\0\145\0\156\0\143\0\171\0\40\0\124\0\145\0\162\0\155\0\163)
- /Parent 37 0 R
+ /Title (\376\377\0\61\0\56\0\67\0\40\0\106\0\141\0\163\0\164\0\40\0\126\0\145\0\143\0\164\0\157\0\162\0\40\0\110\0\151\0\147\0\150\0\154\0\151\0\147\0\150\0\164\0\145\0\162)
+ /Parent 39 0 R
  /Prev 45 0 R
  /Next 47 0 R
- /A 27 0 R
+ /A 23 0 R
 >> endobj
 47 0 obj
 <<
- /Title (\376\377\0\61\0\56\0\61\0\60\0\40\0\111\0\156\0\163\0\164\0\141\0\156\0\164\0\151\0\141\0\164\0\145\0\144\0\111\0\156\0\144\0\145\0\170)
- /Parent 37 0 R
+ /Title (\376\377\0\61\0\56\0\70\0\40\0\112\0\141\0\166\0\141\0\163\0\143\0\162\0\151\0\160\0\164\0\40\0\121\0\165\0\145\0\162\0\171\0\40\0\103\0\157\0\156\0\163\0\164\0\162\0\165\0\143\0\164\0\157\0\162)
+ /Parent 39 0 R
  /Prev 46 0 R
+ /Next 48 0 R
+ /A 25 0 R
+>> endobj
+48 0 obj
+<<
+ /Title (\376\377\0\61\0\56\0\71\0\40\0\112\0\141\0\166\0\141\0\163\0\143\0\162\0\151\0\160\0\164\0\40\0\121\0\165\0\145\0\162\0\171\0\40\0\126\0\141\0\154\0\151\0\144\0\141\0\164\0\157\0\162)
+ /Parent 39 0 R
+ /Prev 47 0 R
+ /Next 49 0 R
+ /A 27 0 R
+>> endobj
+49 0 obj
+<<
+ /Title (\376\377\0\61\0\56\0\61\0\60\0\40\0\110\0\151\0\147\0\150\0\40\0\106\0\162\0\145\0\161\0\165\0\145\0\156\0\143\0\171\0\40\0\124\0\145\0\162\0\155\0\163)
+ /Parent 39 0 R
+ /Prev 48 0 R
+ /Next 50 0 R
  /A 29 0 R
 >> endobj
-48 0 obj
+50 0 obj
+<<
+ /Title (\376\377\0\61\0\56\0\61\0\61\0\40\0\111\0\156\0\163\0\164\0\141\0\156\0\164\0\151\0\141\0\164\0\145\0\144\0\111\0\156\0\144\0\145\0\170)
+ /Parent 39 0 R
+ /Prev 49 0 R
+ /A 31 0 R
+>> endobj
+51 0 obj
 << /Type /Font
 /Subtype /Type1
 /Name /F3
 /BaseFont /Helvetica-Bold
 /Encoding /WinAnsiEncoding >>
 endobj
-49 0 obj
+52 0 obj
 << /Type /Font
 /Subtype /Type1
 /Name /F5
 /BaseFont /Times-Roman
 /Encoding /WinAnsiEncoding >>
 endobj
-50 0 obj
+53 0 obj
 << /Type /Font
 /Subtype /Type1
 /Name /F1
 /BaseFont /Helvetica
 /Encoding /WinAnsiEncoding >>
 endobj
-51 0 obj
+54 0 obj
 << /Type /Font
 /Subtype /Type1
 /Name /F2
 /BaseFont /Helvetica-Oblique
 /Encoding /WinAnsiEncoding >>
 endobj
-52 0 obj
+55 0 obj
 << /Type /Font
 /Subtype /Type1
 /Name /F7
@@ -315,152 +334,161 @@
 1 0 obj
 << /Type /Pages
 /Count 4
-/Kids [6 0 R 31 0 R 33 0 R 35 0 R ] >>
+/Kids [6 0 R 33 0 R 35 0 R 37 0 R ] >>
 endobj
 2 0 obj
 << /Type /Catalog
 /Pages 1 0 R
- /Outlines 36 0 R
+ /Outlines 38 0 R
  /PageMode /UseOutlines
  >>
 endobj
 3 0 obj
 << 
-/Font << /F3 48 0 R /F5 49 0 R /F1 50 0 R /F2 51 0 R /F7 52 0 R >> 
+/Font << /F3 51 0 R /F5 52 0 R /F1 53 0 R /F2 54 0 R /F7 55 0 R >> 
 /ProcSet [ /PDF /ImageC /Text ] >> 
 endobj
 9 0 obj
 <<
 /S /GoTo
-/D [31 0 R /XYZ 85.0 659.0 null]
+/D [33 0 R /XYZ 85.0 659.0 null]
 >>
 endobj
 11 0 obj
 <<
 /S /GoTo
-/D [31 0 R /XYZ 85.0 506.266 null]
+/D [33 0 R /XYZ 85.0 506.266 null]
 >>
 endobj
 13 0 obj
 <<
 /S /GoTo
-/D [31 0 R /XYZ 85.0 399.413 null]
+/D [33 0 R /XYZ 85.0 399.413 null]
 >>
 endobj
 15 0 obj
 <<
 /S /GoTo
-/D [31 0 R /XYZ 85.0 326.96 null]
+/D [33 0 R /XYZ 85.0 326.96 null]
 >>
 endobj
 17 0 obj
 <<
 /S /GoTo
-/D [31 0 R /XYZ 85.0 241.307 null]
+/D [33 0 R /XYZ 85.0 241.307 null]
 >>
 endobj
 19 0 obj
 <<
 /S /GoTo
-/D [33 0 R /XYZ 85.0 603.4 null]
+/D [35 0 R /XYZ 85.0 603.4 null]
 >>
 endobj
 21 0 obj
 <<
 /S /GoTo
-/D [33 0 R /XYZ 85.0 530.947 null]
+/D [35 0 R /XYZ 85.0 530.947 null]
 >>
 endobj
 23 0 obj
 <<
 /S /GoTo
-/D [33 0 R /XYZ 85.0 466.194 null]
+/D [35 0 R /XYZ 85.0 466.194 null]
 >>
 endobj
 25 0 obj
 <<
 /S /GoTo
-/D [33 0 R /XYZ 85.0 380.541 null]
+/D [35 0 R /XYZ 85.0 401.441 null]
 >>
 endobj
 27 0 obj
 <<
 /S /GoTo
-/D [33 0 R /XYZ 85.0 281.688 null]
+/D [35 0 R /XYZ 85.0 315.788 null]
 >>
 endobj
 29 0 obj
 <<
 /S /GoTo
-/D [33 0 R /XYZ 85.0 169.635 null]
+/D [35 0 R /XYZ 85.0 216.935 null]
 >>
 endobj
-36 0 obj
+31 0 obj
 <<
- /First 37 0 R
- /Last 37 0 R
+/S /GoTo
+/D [37 0 R /XYZ 85.0 637.8 null]
+>>
+endobj
+38 0 obj
+<<
+ /First 39 0 R
+ /Last 39 0 R
 >> endobj
 xref
-0 53
+0 56
 0000000000 65535 f 
-0000010600 00000 n 
-0000010679 00000 n 
-0000010771 00000 n 
+0000011212 00000 n 
+0000011291 00000 n 
+0000011383 00000 n 
 0000000015 00000 n 
 0000000071 00000 n 
-0000001019 00000 n 
-0000001139 00000 n 
-0000001234 00000 n 
-0000010894 00000 n 
-0000001369 00000 n 
-0000010957 00000 n 
-0000001506 00000 n 
-0000011023 00000 n 
-0000001643 00000 n 
-0000011089 00000 n 
-0000001778 00000 n 
-0000011154 00000 n 
-0000001915 00000 n 
-0000011220 00000 n 
-0000002051 00000 n 
-0000011284 00000 n 
-0000002188 00000 n 
-0000011350 00000 n 
-0000002324 00000 n 
-0000011416 00000 n 
-0000002459 00000 n 
-0000011482 00000 n 
-0000002596 00000 n 
-0000011548 00000 n 
-0000002733 00000 n 
-0000004696 00000 n 
-0000004804 00000 n 
-0000006695 00000 n 
-0000006803 00000 n 
-0000007293 00000 n 
-0000011614 00000 n 
-0000007401 00000 n 
-0000007602 00000 n 
-0000007869 00000 n 
-0000008161 00000 n 
-0000008295 00000 n 
-0000008506 00000 n 
-0000008838 00000 n 
-0000009049 00000 n 
-0000009331 00000 n 
-0000009601 00000 n 
-0000009835 00000 n 
-0000010044 00000 n 
-0000010157 00000 n 
-0000010267 00000 n 
-0000010375 00000 n 
-0000010491 00000 n 
+0000001068 00000 n 
+0000001188 00000 n 
+0000001290 00000 n 
+0000011506 00000 n 
+0000001425 00000 n 
+0000011569 00000 n 
+0000001562 00000 n 
+0000011635 00000 n 
+0000001699 00000 n 
+0000011701 00000 n 
+0000001834 00000 n 
+0000011766 00000 n 
+0000001971 00000 n 
+0000011832 00000 n 
+0000002107 00000 n 
+0000011896 00000 n 
+0000002244 00000 n 
+0000011962 00000 n 
+0000002381 00000 n 
+0000012028 00000 n 
+0000002517 00000 n 
+0000012094 00000 n 
+0000002652 00000 n 
+0000012160 00000 n 
+0000002789 00000 n 
+0000012226 00000 n 
+0000002926 00000 n 
+0000004889 00000 n 
+0000004997 00000 n 
+0000006840 00000 n 
+0000006948 00000 n 
+0000007648 00000 n 
+0000012290 00000 n 
+0000007756 00000 n 
+0000007957 00000 n 
+0000008224 00000 n 
+0000008516 00000 n 
+0000008650 00000 n 
+0000008861 00000 n 
+0000009193 00000 n 
+0000009404 00000 n 
+0000009656 00000 n 
+0000009938 00000 n 
+0000010208 00000 n 
+0000010447 00000 n 
+0000010656 00000 n 
+0000010769 00000 n 
+0000010879 00000 n 
+0000010987 00000 n 
+0000011103 00000 n 
 trailer
 <<
-/Size 53
+/Size 56
 /Root 2 0 R
 /Info 4 0 R
 >>
 startxref
-11665
+12341
 %%EOF
Index: docs/lucene-sandbox/index.html
===================================================================
--- docs/lucene-sandbox/index.html	(revision 792381)
+++ docs/lucene-sandbox/index.html	(working copy)
@@ -52,7 +52,7 @@
     +-->
 <div class="searchbox">
 <form action="http://search.lucidimagination.com/p:lucene" method="get" class="roundtopsmall">
-<input onFocus="getBlank (this, 'Search the site with google');" size="25" name="q" id="query" type="text" value="Search the site with google">&nbsp; 
+<input onFocus="getBlank (this, 'Search the site with Lucene');" size="25" name="q" id="query" type="text" value="Search the site with Lucene">&nbsp; 
                     <input name="Search" value="Search" type="submit">
 </form>
 <div style="position: relative; top: -5px; left: -10px">Powered by <a href="http://www.lucidimagination.com" style="color: #033268">Lucid Imagination</a>
@@ -144,6 +144,9 @@
 <a href="../api/contrib-collation/index.html">Collation</a>
 </div>
 <div class="menuitem">
+<a href="../api/contrib-fast-vector-highlighter/index.html">Fast Vector Highlighter</a>
+</div>
+<div class="menuitem">
 <a href="../api/contrib-highlighter/index.html">Highlighter</a>
 </div>
 <div class="menuitem">
@@ -270,6 +273,9 @@
 <a href="#Term Highlighter">Term Highlighter</a>
 </li>
 <li>
+<a href="#Fast Vector Highlighter">Fast Vector Highlighter</a>
+</li>
+<li>
 <a href="#Javascript Query Constructor">Javascript Query Constructor</a>
 </li>
 <li>
@@ -381,7 +387,13 @@
                     search results.
                 </p>
 <a href="http://svn.apache.org/repos/asf/lucene/java/trunk/contrib/highlighter/">The
-                        repository for the Highlighter contribution.</a><a name="N10092"></a><a name="Javascript Query Constructor"></a>
+                        repository for the Highlighter contribution.</a><a name="N10092"></a><a name="Fast Vector Highlighter"></a>
+<h3 class="boxed">Fast Vector Highlighter</h3>
+<p>
+                  An alternative set of classes for highlighting matching terms in search results.
+                </p>
+<a href="http://svn.apache.org/repos/asf/lucene/java/trunk/contrib/fast-vector-highlighter/">The
+                        repository for the Fast Vector Highlighter contribution.</a><a name="N1009F"></a><a name="Javascript Query Constructor"></a>
 <h3 class="boxed">Javascript Query Constructor</h3>
 <p>
                     Javascript library to support client-side query-building. Provides support for a user interface similar to
@@ -394,7 +406,7 @@
                         repository for the Javascript Query Constructor files.</a>
                 
 </p>
-<a name="N100A6"></a><a name="Javascript Query Validator"></a>
+<a name="N100B3"></a><a name="Javascript Query Validator"></a>
 <h3 class="boxed">Javascript Query Validator</h3>
 <p>
                     Javascript library to support client-side query validation. Lucene doesn't like malformed queries and tends to
@@ -408,7 +420,7 @@
                         repository for the Javascript Query Validator files.</a>
                 
 </p>
-<a name="N100B6"></a><a name="High Frequency Terms"></a>
+<a name="N100C3"></a><a name="High Frequency Terms"></a>
 <h3 class="boxed">High Frequency Terms</h3>
 <p>
                     The miscellaneous package is for classes that don't fit anywhere else. The only class in it right now determines
@@ -422,7 +434,7 @@
                         repository for miscellaneous classes.</a>
                 
 </p>
-<a name="N100C6"></a><a name="InstantiatedIndex"></a>
+<a name="N100D3"></a><a name="InstantiatedIndex"></a>
 <h3 class="boxed">InstantiatedIndex</h3>
 <p>
                    RAM-based index that enables much faster searching than RAMDirectory.
Index: docs/scoring.html
===================================================================
--- docs/scoring.html	(revision 792381)
+++ docs/scoring.html	(working copy)
@@ -52,7 +52,7 @@
     +-->
 <div class="searchbox">
 <form action="http://search.lucidimagination.com/p:lucene" method="get" class="roundtopsmall">
-<input onFocus="getBlank (this, 'Search the site with google');" size="25" name="q" id="query" type="text" value="Search the site with google">&nbsp; 
+<input onFocus="getBlank (this, 'Search the site with Lucene');" size="25" name="q" id="query" type="text" value="Search the site with Lucene">&nbsp; 
                     <input name="Search" value="Search" type="submit">
 </form>
 <div style="position: relative; top: -5px; left: -10px">Powered by <a href="http://www.lucidimagination.com" style="color: #033268">Lucid Imagination</a>
@@ -144,6 +144,9 @@
 <a href="api/contrib-collation/index.html">Collation</a>
 </div>
 <div class="menuitem">
+<a href="api/contrib-fast-vector-highlighter/index.html">Fast Vector Highlighter</a>
+</div>
+<div class="menuitem">
 <a href="api/contrib-highlighter/index.html">Highlighter</a>
 </div>
 <div class="menuitem">
Index: docs/demo2.html
===================================================================
--- docs/demo2.html	(revision 792381)
+++ docs/demo2.html	(working copy)
@@ -52,7 +52,7 @@
     +-->
 <div class="searchbox">
 <form action="http://search.lucidimagination.com/p:lucene" method="get" class="roundtopsmall">
-<input onFocus="getBlank (this, 'Search the site with google');" size="25" name="q" id="query" type="text" value="Search the site with google">&nbsp; 
+<input onFocus="getBlank (this, 'Search the site with Lucene');" size="25" name="q" id="query" type="text" value="Search the site with Lucene">&nbsp; 
                     <input name="Search" value="Search" type="submit">
 </form>
 <div style="position: relative; top: -5px; left: -10px">Powered by <a href="http://www.lucidimagination.com" style="color: #033268">Lucid Imagination</a>
@@ -144,6 +144,9 @@
 <a href="api/contrib-collation/index.html">Collation</a>
 </div>
 <div class="menuitem">
+<a href="api/contrib-fast-vector-highlighter/index.html">Fast Vector Highlighter</a>
+</div>
+<div class="menuitem">
 <a href="api/contrib-highlighter/index.html">Highlighter</a>
 </div>
 <div class="menuitem">
Index: docs/demo3.html
===================================================================
--- docs/demo3.html	(revision 792381)
+++ docs/demo3.html	(working copy)
@@ -52,7 +52,7 @@
     +-->
 <div class="searchbox">
 <form action="http://search.lucidimagination.com/p:lucene" method="get" class="roundtopsmall">
-<input onFocus="getBlank (this, 'Search the site with google');" size="25" name="q" id="query" type="text" value="Search the site with google">&nbsp; 
+<input onFocus="getBlank (this, 'Search the site with Lucene');" size="25" name="q" id="query" type="text" value="Search the site with Lucene">&nbsp; 
                     <input name="Search" value="Search" type="submit">
 </form>
 <div style="position: relative; top: -5px; left: -10px">Powered by <a href="http://www.lucidimagination.com" style="color: #033268">Lucid Imagination</a>
@@ -144,6 +144,9 @@
 <a href="api/contrib-collation/index.html">Collation</a>
 </div>
 <div class="menuitem">
+<a href="api/contrib-fast-vector-highlighter/index.html">Fast Vector Highlighter</a>
+</div>
+<div class="menuitem">
 <a href="api/contrib-highlighter/index.html">Highlighter</a>
 </div>
 <div class="menuitem">
Index: docs/index.html
===================================================================
--- docs/index.html	(revision 792381)
+++ docs/index.html	(working copy)
@@ -50,7 +50,7 @@
     +-->
 <div class="searchbox">
 <form action="http://search.lucidimagination.com/p:lucene" method="get" class="roundtopsmall">
-<input onFocus="getBlank (this, 'Search the site with google');" size="25" name="q" id="query" type="text" value="Search the site with google">&nbsp; 
+<input onFocus="getBlank (this, 'Search the site with Lucene');" size="25" name="q" id="query" type="text" value="Search the site with Lucene">&nbsp; 
                     <input name="Search" value="Search" type="submit">
 </form>
 <div style="position: relative; top: -5px; left: -10px">Powered by <a href="http://www.lucidimagination.com" style="color: #033268">Lucid Imagination</a>
@@ -142,6 +142,9 @@
 <a href="api/contrib-collation/index.html">Collation</a>
 </div>
 <div class="menuitem">
+<a href="api/contrib-fast-vector-highlighter/index.html">Fast Vector Highlighter</a>
+</div>
+<div class="menuitem">
 <a href="api/contrib-highlighter/index.html">Highlighter</a>
 </div>
 <div class="menuitem">
Index: docs/demo4.html
===================================================================
--- docs/demo4.html	(revision 792381)
+++ docs/demo4.html	(working copy)
@@ -52,7 +52,7 @@
     +-->
 <div class="searchbox">
 <form action="http://search.lucidimagination.com/p:lucene" method="get" class="roundtopsmall">
-<input onFocus="getBlank (this, 'Search the site with google');" size="25" name="q" id="query" type="text" value="Search the site with google">&nbsp; 
+<input onFocus="getBlank (this, 'Search the site with Lucene');" size="25" name="q" id="query" type="text" value="Search the site with Lucene">&nbsp; 
                     <input name="Search" value="Search" type="submit">
 </form>
 <div style="position: relative; top: -5px; left: -10px">Powered by <a href="http://www.lucidimagination.com" style="color: #033268">Lucid Imagination</a>
@@ -144,6 +144,9 @@
 <a href="api/contrib-collation/index.html">Collation</a>
 </div>
 <div class="menuitem">
+<a href="api/contrib-fast-vector-highlighter/index.html">Fast Vector Highlighter</a>
+</div>
+<div class="menuitem">
 <a href="api/contrib-highlighter/index.html">Highlighter</a>
 </div>
 <div class="menuitem">
Index: docs/fileformats.html
===================================================================
--- docs/fileformats.html	(revision 792381)
+++ docs/fileformats.html	(working copy)
@@ -52,7 +52,7 @@
     +-->
 <div class="searchbox">
 <form action="http://search.lucidimagination.com/p:lucene" method="get" class="roundtopsmall">
-<input onFocus="getBlank (this, 'Search the site with google');" size="25" name="q" id="query" type="text" value="Search the site with google">&nbsp; 
+<input onFocus="getBlank (this, 'Search the site with Lucene');" size="25" name="q" id="query" type="text" value="Search the site with Lucene">&nbsp; 
                     <input name="Search" value="Search" type="submit">
 </form>
 <div style="position: relative; top: -5px; left: -10px">Powered by <a href="http://www.lucidimagination.com" style="color: #033268">Lucid Imagination</a>
@@ -144,6 +144,9 @@
 <a href="api/contrib-collation/index.html">Collation</a>
 </div>
 <div class="menuitem">
+<a href="api/contrib-fast-vector-highlighter/index.html">Fast Vector Highlighter</a>
+</div>
+<div class="menuitem">
 <a href="api/contrib-highlighter/index.html">Highlighter</a>
 </div>
 <div class="menuitem">
Index: build.xml
===================================================================
--- build.xml	(revision 792381)
+++ build.xml	(working copy)
@@ -312,6 +312,7 @@
           <packageset dir="contrib/collation/src/java"/>
           <packageset dir="contrib/db/bdb-je/src/java"/>
           <packageset dir="contrib/db/bdb/src/java"/>
+          <packageset dir="contrib/fast-vector-highlighter/src/java"/>
           <packageset dir="contrib/highlighter/src/java"/>
           <packageset dir="contrib/instantiated/src/java"/>
           <packageset dir="contrib/lucli/src/java"/>
@@ -343,6 +344,7 @@
           <group title="contrib: Benchmark" packages="org.apache.lucene.benchmark*"/>
           <group title="contrib: Collation" packages="org.apache.lucene.collation*"/>
           <group title="contrib: DB" packages="org.apache.lucene.store.db*:org.apache.lucene.store.je*:com.sleepycat*"/>
+          <group title="contrib: Fast Vector Highlighter" packages="org.apache.lucene.search.vectorhighlight*"/>
           <group title="contrib: Highlighter" packages="org.apache.lucene.search.highlight*"/>
           <group title="contrib: Instantiated" packages="org.apache.lucene.store.instantiated*"/>
           <group title="contrib: Lucli" packages="lucli*"/>
Index: contrib/CHANGES.txt
===================================================================
--- contrib/CHANGES.txt	(revision 792381)
+++ contrib/CHANGES.txt	(working copy)
@@ -65,6 +65,9 @@
  7. LUCENE-1704: Allow specifying the Tidy configuration file when
     parsing HTML docs with contrib/ant.  (Keith Sprochi via Mike
     McCandless)
+
+ 8. LUCENE-1522: Added contrib/fast-vector-highlighter, a new alternative
+    highlighter.  (Koji Sekiguchi via Mike McCandless)
  
 Optimizations
 
Index: contrib/fast-vector-highlighter/src/test/org/apache/lucene/search/vectorhighlight/AbstractTestCase.java
===================================================================
--- contrib/fast-vector-highlighter/src/test/org/apache/lucene/search/vectorhighlight/AbstractTestCase.java	(revision 0)
+++ contrib/fast-vector-highlighter/src/test/org/apache/lucene/search/vectorhighlight/AbstractTestCase.java	(revision 0)
@@ -0,0 +1,345 @@
+package org.apache.lucene.search.vectorhighlight;
+
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import java.io.IOException;
+import java.io.Reader;
+import java.util.Collection;
+
+import junit.framework.TestCase;
+
+import org.apache.lucene.analysis.Analyzer;
+import org.apache.lucene.analysis.Token;
+import org.apache.lucene.analysis.TokenStream;
+import org.apache.lucene.analysis.Tokenizer;
+import org.apache.lucene.analysis.WhitespaceAnalyzer;
+import org.apache.lucene.document.Document;
+import org.apache.lucene.document.Field;
+import org.apache.lucene.document.Field.Index;
+import org.apache.lucene.document.Field.Store;
+import org.apache.lucene.document.Field.TermVector;
+import org.apache.lucene.index.IndexReader;
+import org.apache.lucene.index.IndexWriter;
+import org.apache.lucene.index.Term;
+import org.apache.lucene.index.IndexWriter.MaxFieldLength;
+import org.apache.lucene.queryParser.QueryParser;
+import org.apache.lucene.search.PhraseQuery;
+import org.apache.lucene.search.Query;
+import org.apache.lucene.search.TermQuery;
+import org.apache.lucene.store.Directory;
+import org.apache.lucene.store.RAMDirectory;
+
+public abstract class AbstractTestCase extends TestCase {
+
+  protected final String F = "f";
+  protected final String F1 = "f1";
+  protected final String F2 = "f2";
+  protected Directory dir;
+  protected Analyzer analyzerW;
+  protected Analyzer analyzerB;
+  protected IndexReader reader;  
+  protected QueryParser paW;
+  protected QueryParser paB;
+  
+  protected static final String[] shortMVValues = {
+    "a b c",
+    "",   // empty data in multi valued field
+    "d e"
+  };
+  
+  protected static final String[] longMVValues = {
+    "Followings are the examples of customizable parameters and actual examples of customization:",
+    "The most search engines use only one of these methods. Even the search engines that says they can use the both methods basically"
+  };
+  
+  // test data for LUCENE-1448 bug
+  protected static final String[] biMVValues = {
+    "\nLucene/Solr does not require such additional hardware.",
+    "\nWhen you talk about processing speed, the"
+  };
+
+  protected void setUp() throws Exception {
+    analyzerW = new WhitespaceAnalyzer();
+    analyzerB = new BigramAnalyzer();
+    paW = new QueryParser( F, analyzerW );
+    paB = new QueryParser( F, analyzerB );
+    dir = new RAMDirectory();
+  }
+  
+  protected void tearDown() throws Exception {
+    if( reader != null ){
+      reader.close();
+      reader = null;
+    }
+  }
+
+  protected Query tq( String text ){
+    return tq( 1F, text );
+  }
+
+  protected Query tq( float boost, String text ){
+    return tq( boost, F, text );
+  }
+  
+  protected Query tq( String field, String text ){
+    return tq( 1F, field, text );
+  }
+  
+  protected Query tq( float boost, String field, String text ){
+    Query query = new TermQuery( new Term( field, text ) );
+    query.setBoost( boost );
+    return query;
+  }
+  
+  protected Query pqF( String... texts ){
+    return pqF( 1F, texts );
+  }
+  
+  protected Query pqF( float boost, String... texts ){
+    return pqF( boost, 0, texts );
+  }
+  
+  protected Query pqF( float boost, int slop, String... texts ){
+    return pq( boost, slop, F, texts );
+  }
+  
+  protected Query pq( String field, String... texts ){
+    return pq( 1F, 0, field, texts );
+  }
+  
+  protected Query pq( float boost, String field, String... texts ){
+    return pq( boost, 0, field, texts );
+  }
+  
+  protected Query pq( float boost, int slop, String field, String... texts ){
+    PhraseQuery query = new PhraseQuery();
+    for( String text : texts ){
+      query.add( new Term( field, text ) );
+    }
+    query.setBoost( boost );
+    query.setSlop( slop );
+    return query;
+  }
+  
+  protected void assertCollectionQueries( Collection<Query> actual, Query... expected ){
+    assertEquals( expected.length, actual.size() );
+    for( Query query : expected ){
+      assertTrue( actual.contains( query ) );
+    }
+  }
+
+  static class BigramAnalyzer extends Analyzer {
+    public TokenStream tokenStream(String fieldName, Reader reader) {
+      return new BasicNGramTokenizer( reader );
+    }
+  }
+  
+  static class BasicNGramTokenizer extends Tokenizer {
+
+    public static final int DEFAULT_N_SIZE = 2;
+    public static final String DEFAULT_DELIMITERS = " \t\n.,";
+    private final int n;
+    private final String delimiters;
+    private int startTerm;
+    private int lenTerm;
+    private int startOffset;
+    private int nextStartOffset;
+    private int ch;
+    private String snippet;
+    private StringBuilder snippetBuffer;
+    private static final int BUFFER_SIZE = 4096;
+    private char[] charBuffer;
+    private int charBufferIndex;
+    private int charBufferLen;
+    
+    public BasicNGramTokenizer( Reader in ){
+      this( in, DEFAULT_N_SIZE );
+    }
+    
+    public BasicNGramTokenizer( Reader in, int n ){
+      this( in, n, DEFAULT_DELIMITERS );
+    }
+    
+    public BasicNGramTokenizer( Reader in, String delimiters ){
+      this( in, DEFAULT_N_SIZE, delimiters );
+    }
+    
+    public BasicNGramTokenizer( Reader in, int n, String delimiters ){
+      super(in);
+      this.n = n;
+      this.delimiters = delimiters;
+      startTerm = 0;
+      nextStartOffset = 0;
+      snippet = null;
+      snippetBuffer = new StringBuilder();
+      charBuffer = new char[BUFFER_SIZE];
+      charBufferIndex = BUFFER_SIZE;
+      charBufferLen = 0;
+      ch = 0;
+    }
+
+    public Token next( Token reusableToken ) throws IOException {
+      if( !getNextPartialSnippet() )
+        return null;
+      reusableToken.reinit( snippet, startTerm, lenTerm, startOffset, startOffset + lenTerm );
+      return reusableToken;
+    }
+
+    public int getFinalOffset() {
+      return nextStartOffset;
+    }
+    
+    protected boolean getNextPartialSnippet() throws IOException {
+      if( snippet != null && snippet.length() >= startTerm + 1 + n ){
+        startTerm++;
+        startOffset++;
+        lenTerm = n;
+        return true;
+      }
+      return getNextSnippet();
+    }
+    
+    protected boolean getNextSnippet() throws IOException {
+      startTerm = 0;
+      startOffset = nextStartOffset;
+      snippetBuffer.delete( 0, snippetBuffer.length() );
+      while( true ){
+        if( ch != -1 )
+          ch = readCharFromBuffer();
+        if( ch == -1 ) break;
+        else if( !isDelimiter( ch ) )
+          snippetBuffer.append( (char)ch );
+        else if( snippetBuffer.length() > 0 )
+          break;
+        else
+          startOffset++;
+      }
+      if( snippetBuffer.length() == 0 )
+        return false;
+      snippet = snippetBuffer.toString();
+      lenTerm = snippet.length() >= n ? n : snippet.length();
+      return true;
+    }
+    
+    protected int readCharFromBuffer() throws IOException {
+      if( charBufferIndex >= charBufferLen ){
+        charBufferLen = input.read( charBuffer );
+        if( charBufferLen == -1 ){
+          return -1;
+        }
+        charBufferIndex = 0;
+      }
+      int c = (int)charBuffer[charBufferIndex++];
+      nextStartOffset++;
+      return c;
+    }
+    
+    protected boolean isDelimiter( int c ){
+      return delimiters.indexOf( c ) >= 0;
+    }
+  }
+
+  protected void make1d1fIndex( String value ) throws Exception {
+    make1dmfIndex( value );
+  }
+  
+  protected void make1d1fIndexB( String value ) throws Exception {
+    make1dmfIndexB( value );
+  }
+  
+  protected void make1dmfIndex( String... values ) throws Exception {
+    make1dmfIndex( analyzerW, values );
+  }
+  
+  protected void make1dmfIndexB( String... values ) throws Exception {
+    make1dmfIndex( analyzerB, values );
+  }
+  
+  protected void make1dmfIndex( Analyzer analyzer, String... values ) throws Exception {
+    IndexWriter writer = new IndexWriter( dir, analyzer, true, MaxFieldLength.LIMITED );
+    Document doc = new Document();
+    for( String value: values )
+      doc.add( new Field( F, value, Store.YES, Index.ANALYZED, TermVector.WITH_POSITIONS_OFFSETS ) );
+    writer.addDocument( doc );
+    writer.close();
+
+    reader = IndexReader.open( dir );
+  }
+  
+  protected void makeIndexShortMV() throws Exception {
+
+    //  012345
+    // "a b c"
+    //  0 1 2
+    
+    // ""
+
+    //  6789
+    // "d e"
+    //  3 4
+    make1dmfIndex( shortMVValues );
+  }
+  
+  protected void makeIndexLongMV() throws Exception {
+    //           11111111112222222222333333333344444444445555555555666666666677777777778888888888999
+    // 012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012
+    // Followings are the examples of customizable parameters and actual examples of customization:
+    // 0          1   2   3        4  5            6          7   8      9        10 11
+    
+    //        1                                                                                                   2
+    // 999999900000000001111111111222222222233333333334444444444555555555566666666667777777777888888888899999999990000000000111111111122
+    // 345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901
+    // The most search engines use only one of these methods. Even the search engines that says they can use the both methods basically
+    // 12  13  (14)   (15)     16  17   18  19 20    21       22   23 (24)   (25)     26   27   28   29  30  31  32   33      34
+
+    make1dmfIndex( longMVValues );
+  }
+  
+  protected void makeIndexLongMVB() throws Exception {
+    // "*" ... LF
+    
+    //           1111111111222222222233333333334444444444555555
+    // 01234567890123456789012345678901234567890123456789012345
+    // *Lucene/Solr does not require such additional hardware.
+    //  Lu 0        do 10    re 15   su 21       na 31
+    //   uc 1        oe 11    eq 16   uc 22       al 32
+    //    ce 2        es 12    qu 17   ch 23         ha 33
+    //     en 3          no 13  ui 18     ad 24       ar 34
+    //      ne 4          ot 14  ir 19     dd 25       rd 35
+    //       e/ 5                 re 20     di 26       dw 36
+    //        /S 6                           it 27       wa 37
+    //         So 7                           ti 28       ar 38
+    //          ol 8                           io 29       re 39
+    //           lr 9                           on 30
+
+    // 5555666666666677777777778888888888999999999
+    // 6789012345678901234567890123456789012345678
+    // *When you talk about processing speed, the
+    //  Wh 40         ab 48     es 56         th 65
+    //   he 41         bo 49     ss 57         he 66
+    //    en 42         ou 50     si 58
+    //       yo 43       ut 51     in 59
+    //        ou 44         pr 52   ng 60
+    //           ta 45       ro 53     sp 61
+    //            al 46       oc 54     pe 62
+    //             lk 47       ce 55     ee 63
+    //                                    ed 64
+
+    make1dmfIndexB( biMVValues );
+  }
+}

Property changes on: contrib/fast-vector-highlighter/src/test/org/apache/lucene/search/vectorhighlight/AbstractTestCase.java
___________________________________________________________________
Name: svn:keywords
   + Date Author Id Revision HeadURL
Name: svn:eol-style
   + native

Index: contrib/fast-vector-highlighter/src/test/org/apache/lucene/search/vectorhighlight/FieldTermStackTest.java
===================================================================
--- contrib/fast-vector-highlighter/src/test/org/apache/lucene/search/vectorhighlight/FieldTermStackTest.java	(revision 0)
+++ contrib/fast-vector-highlighter/src/test/org/apache/lucene/search/vectorhighlight/FieldTermStackTest.java	(revision 0)
@@ -0,0 +1,166 @@
+package org.apache.lucene.search.vectorhighlight;
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import org.apache.lucene.search.BooleanQuery;
+import org.apache.lucene.search.BooleanClause.Occur;
+
+public class FieldTermStackTest extends AbstractTestCase {
+  
+  public void test1Term() throws Exception {
+    makeIndex();
+    
+    FieldQuery fq = new FieldQuery( tq( "a" ), true, true );
+    FieldTermStack stack = new FieldTermStack( reader, 0, F, fq );
+    assertEquals( 6, stack.termList.size() );
+    assertEquals( "a(0,1,0)", stack.pop().toString() );
+    assertEquals( "a(2,3,1)", stack.pop().toString() );
+    assertEquals( "a(4,5,2)", stack.pop().toString() );
+    assertEquals( "a(12,13,6)", stack.pop().toString() );
+    assertEquals( "a(28,29,14)", stack.pop().toString() );
+    assertEquals( "a(32,33,16)", stack.pop().toString() );
+  }
+  
+  public void test2Terms() throws Exception {
+    makeIndex();
+    
+    BooleanQuery query = new BooleanQuery();
+    query.add( tq( "b" ), Occur.SHOULD );
+    query.add( tq( "c" ), Occur.SHOULD );
+    FieldQuery fq = new FieldQuery( query, true, true );
+    FieldTermStack stack = new FieldTermStack( reader, 0, F, fq );
+    assertEquals( 8, stack.termList.size() );
+    assertEquals( "b(6,7,3)", stack.pop().toString() );
+    assertEquals( "b(8,9,4)", stack.pop().toString() );
+    assertEquals( "c(10,11,5)", stack.pop().toString() );
+    assertEquals( "b(14,15,7)", stack.pop().toString() );
+    assertEquals( "b(16,17,8)", stack.pop().toString() );
+    assertEquals( "c(18,19,9)", stack.pop().toString() );
+    assertEquals( "b(26,27,13)", stack.pop().toString() );
+    assertEquals( "b(30,31,15)", stack.pop().toString() );
+  }
+  
+  public void test1Phrase() throws Exception {
+    makeIndex();
+    
+    FieldQuery fq = new FieldQuery( pqF( "c", "d" ), true, true );
+    FieldTermStack stack = new FieldTermStack( reader, 0, F, fq );
+    assertEquals( 3, stack.termList.size() );
+    assertEquals( "c(10,11,5)", stack.pop().toString() );
+    assertEquals( "c(18,19,9)", stack.pop().toString() );
+    assertEquals( "d(20,21,10)", stack.pop().toString() );
+  }
+  
+  private void makeIndex() throws Exception {
+    //           111111111122222
+    // 0123456789012345678901234 (offsets)
+    // a a a b b c a b b c d e f
+    // 0 1 2 3 4 5 6 7 8 9101112 (position)
+    String value1 = "a a a b b c a b b c d e f";
+    // 222233333
+    // 678901234 (offsets)
+    // b a b a f
+    //1314151617 (position)
+    String value2 = "b a b a f";
+    
+    make1dmfIndex( value1, value2 );
+  }
+  
+  public void test1TermB() throws Exception {
+    makeIndexB();
+    
+    FieldQuery fq = new FieldQuery( tq( "ab" ), true, true );
+    FieldTermStack stack = new FieldTermStack( reader, 0, F, fq );
+    assertEquals( 2, stack.termList.size() );
+    assertEquals( "ab(2,4,2)", stack.pop().toString() );
+    assertEquals( "ab(6,8,6)", stack.pop().toString() );
+  }
+  
+  public void test2TermsB() throws Exception {
+    makeIndexB();
+    
+    BooleanQuery query = new BooleanQuery();
+    query.add( tq( "bc" ), Occur.SHOULD );
+    query.add( tq( "ef" ), Occur.SHOULD );
+    FieldQuery fq = new FieldQuery( query, true, true );
+    FieldTermStack stack = new FieldTermStack( reader, 0, F, fq );
+    assertEquals( 3, stack.termList.size() );
+    assertEquals( "bc(4,6,4)", stack.pop().toString() );
+    assertEquals( "bc(8,10,8)", stack.pop().toString() );
+    assertEquals( "ef(11,13,11)", stack.pop().toString() );
+  }
+  
+  public void test1PhraseB() throws Exception {
+    makeIndexB();
+    
+    FieldQuery fq = new FieldQuery( pqF( "ab", "bb" ), true, true );
+    FieldTermStack stack = new FieldTermStack( reader, 0, F, fq );
+    assertEquals( 4, stack.termList.size() );
+    assertEquals( "ab(2,4,2)", stack.pop().toString() );
+    assertEquals( "bb(3,5,3)", stack.pop().toString() );
+    assertEquals( "ab(6,8,6)", stack.pop().toString() );
+    assertEquals( "bb(7,9,7)", stack.pop().toString() );
+  }
+  
+  private void makeIndexB() throws Exception {
+    //                             1 11 11
+    // 01 12 23 34 45 56 67 78 89 90 01 12 (offsets)
+    // aa|aa|ab|bb|bc|ca|ab|bb|bc|cd|de|ef
+    //  0  1  2  3  4  5  6  7  8  9 10 11 (position)
+    String value = "aaabbcabbcdef";
+    
+    make1dmfIndexB( value );
+  }
+  
+  public void test1PhraseShortMV() throws Exception {
+    makeIndexShortMV();
+    
+    FieldQuery fq = new FieldQuery( tq( "d" ), true, true );
+    FieldTermStack stack = new FieldTermStack( reader, 0, F, fq );
+    assertEquals( 1, stack.termList.size() );
+    assertEquals( "d(6,7,3)", stack.pop().toString() );
+  }
+  
+  public void test1PhraseLongMV() throws Exception {
+    makeIndexLongMV();
+    
+    FieldQuery fq = new FieldQuery( pqF( "search", "engines" ), true, true );
+    FieldTermStack stack = new FieldTermStack( reader, 0, F, fq );
+    assertEquals( 4, stack.termList.size() );
+    assertEquals( "search(102,108,14)", stack.pop().toString() );
+    assertEquals( "engines(109,116,15)", stack.pop().toString() );
+    assertEquals( "search(157,163,24)", stack.pop().toString() );
+    assertEquals( "engines(164,171,25)", stack.pop().toString() );
+  }
+/*
+ * ----------------------------------
+ *  THIS TEST DEPENDS ON LUCENE-1448
+ *  UNCOMMENT WHEN IT IS COMMITTED.
+ * ----------------------------------
+  public void test1PhraseMVB() throws Exception {
+    makeIndexLongMVB();
+    
+    FieldQuery fq = new FieldQuery( pqF( "sp", "pe", "ee", "ed" ), true, true ); // "speed" -(2gram)-> "sp","pe","ee","ed"
+    FieldTermStack stack = new FieldTermStack( reader, 0, F, fq );
+    assertEquals( 4, stack.termList.size() );
+    assertEquals( "sp(88,90,61)", stack.pop().toString() );
+    assertEquals( "pe(89,91,62)", stack.pop().toString() );
+    assertEquals( "ee(90,92,63)", stack.pop().toString() );
+    assertEquals( "ed(91,93,64)", stack.pop().toString() );
+  }
+*/
+}

Property changes on: contrib/fast-vector-highlighter/src/test/org/apache/lucene/search/vectorhighlight/FieldTermStackTest.java
___________________________________________________________________
Name: svn:keywords
   + Date Author Id Revision HeadURL
Name: svn:eol-style
   + native

Index: contrib/fast-vector-highlighter/src/test/org/apache/lucene/search/vectorhighlight/FieldPhraseListTest.java
===================================================================
--- contrib/fast-vector-highlighter/src/test/org/apache/lucene/search/vectorhighlight/FieldPhraseListTest.java	(revision 0)
+++ contrib/fast-vector-highlighter/src/test/org/apache/lucene/search/vectorhighlight/FieldPhraseListTest.java	(revision 0)
@@ -0,0 +1,182 @@
+package org.apache.lucene.search.vectorhighlight;
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import org.apache.lucene.search.BooleanQuery;
+import org.apache.lucene.search.BooleanClause.Occur;
+
+public class FieldPhraseListTest extends AbstractTestCase {
+  
+  public void test1TermIndex() throws Exception {
+    make1d1fIndex( "a" );
+
+    FieldQuery fq = new FieldQuery( tq( "a" ), true, true );
+    FieldTermStack stack = new FieldTermStack( reader, 0, F, fq );
+    FieldPhraseList fpl = new FieldPhraseList( stack, fq );
+    assertEquals( 1, fpl.phraseList.size() );
+    assertEquals( "a(1.0)((0,1))", fpl.phraseList.get( 0 ).toString() );
+
+    fq = new FieldQuery( tq( "b" ), true, true );
+    stack = new FieldTermStack( reader, 0, F, fq );
+    fpl = new FieldPhraseList( stack, fq );
+    assertEquals( 0, fpl.phraseList.size() );
+  }
+  
+  public void test2TermsIndex() throws Exception {
+    make1d1fIndex( "a a" );
+
+    FieldQuery fq = new FieldQuery( tq( "a" ), true, true );
+    FieldTermStack stack = new FieldTermStack( reader, 0, F, fq );
+    FieldPhraseList fpl = new FieldPhraseList( stack, fq );
+    assertEquals( 2, fpl.phraseList.size() );
+    assertEquals( "a(1.0)((0,1))", fpl.phraseList.get( 0 ).toString() );
+    assertEquals( "a(1.0)((2,3))", fpl.phraseList.get( 1 ).toString() );
+  }
+  
+  public void test1PhraseIndex() throws Exception {
+    make1d1fIndex( "a b" );
+
+    FieldQuery fq = new FieldQuery( pqF( "a", "b" ), true, true );
+    FieldTermStack stack = new FieldTermStack( reader, 0, F, fq );
+    FieldPhraseList fpl = new FieldPhraseList( stack, fq );
+    assertEquals( 1, fpl.phraseList.size() );
+    assertEquals( "ab(1.0)((0,3))", fpl.phraseList.get( 0 ).toString() );
+
+    fq = new FieldQuery( tq( "b" ), true, true );
+    stack = new FieldTermStack( reader, 0, F, fq );
+    fpl = new FieldPhraseList( stack, fq );
+    assertEquals( 1, fpl.phraseList.size() );
+    assertEquals( "b(1.0)((2,3))", fpl.phraseList.get( 0 ).toString() );
+  }
+  
+  public void test1PhraseIndexB() throws Exception {
+    // 01 12 23 34 45 56 67 78 (offsets)
+    // bb|bb|ba|ac|cb|ba|ab|bc
+    //  0  1  2  3  4  5  6  7 (positions)
+    make1d1fIndexB( "bbbacbabc" );
+
+    FieldQuery fq = new FieldQuery( pqF( "ba", "ac" ), true, true );
+    FieldTermStack stack = new FieldTermStack( reader, 0, F, fq );
+    FieldPhraseList fpl = new FieldPhraseList( stack, fq );
+    assertEquals( 1, fpl.phraseList.size() );
+    assertEquals( "baac(1.0)((2,5))", fpl.phraseList.get( 0 ).toString() );
+  }
+  
+  public void test2Terms1PhraseIndex() throws Exception {
+    make1d1fIndex( "c a a b" );
+
+    // phraseHighlight = true
+    FieldQuery fq = new FieldQuery( pqF( "a", "b" ), true, true );
+    FieldTermStack stack = new FieldTermStack( reader, 0, F, fq );
+    FieldPhraseList fpl = new FieldPhraseList( stack, fq );
+    assertEquals( 1, fpl.phraseList.size() );
+    assertEquals( "ab(1.0)((4,7))", fpl.phraseList.get( 0 ).toString() );
+
+    // phraseHighlight = false
+    fq = new FieldQuery( pqF( "a", "b" ), false, true );
+    stack = new FieldTermStack( reader, 0, F, fq );
+    fpl = new FieldPhraseList( stack, fq );
+    assertEquals( 2, fpl.phraseList.size() );
+    assertEquals( "a(1.0)((2,3))", fpl.phraseList.get( 0 ).toString() );
+    assertEquals( "ab(1.0)((4,7))", fpl.phraseList.get( 1 ).toString() );
+  }
+  
+  public void testPhraseSlop() throws Exception {
+    make1d1fIndex( "c a a b c" );
+
+    FieldQuery fq = new FieldQuery( pqF( 2F, 1, "a", "c" ), true, true );
+    FieldTermStack stack = new FieldTermStack( reader, 0, F, fq );
+    FieldPhraseList fpl = new FieldPhraseList( stack, fq );
+    assertEquals( 1, fpl.phraseList.size() );
+    assertEquals( "ac(2.0)((4,5)(8,9))", fpl.phraseList.get( 0 ).toString() );
+    assertEquals( 4, fpl.phraseList.get( 0 ).getStartOffset() );
+    assertEquals( 9, fpl.phraseList.get( 0 ).getEndOffset() );
+  }
+  
+  public void test2PhrasesOverlap() throws Exception {
+    make1d1fIndex( "d a b c d" );
+
+    BooleanQuery query = new BooleanQuery();
+    query.add( pqF( "a", "b" ), Occur.SHOULD );
+    query.add( pqF( "b", "c" ), Occur.SHOULD );
+    FieldQuery fq = new FieldQuery( query, true, true );
+    FieldTermStack stack = new FieldTermStack( reader, 0, F, fq );
+    FieldPhraseList fpl = new FieldPhraseList( stack, fq );
+    assertEquals( 1, fpl.phraseList.size() );
+    assertEquals( "abc(1.0)((2,7))", fpl.phraseList.get( 0 ).toString() );
+  }
+  
+  public void test3TermsPhrase() throws Exception {
+    make1d1fIndex( "d a b a b c d" );
+
+    FieldQuery fq = new FieldQuery( pqF( "a", "b", "c" ), true, true );
+    FieldTermStack stack = new FieldTermStack( reader, 0, F, fq );
+    FieldPhraseList fpl = new FieldPhraseList( stack, fq );
+    assertEquals( 1, fpl.phraseList.size() );
+    assertEquals( "abc(1.0)((6,11))", fpl.phraseList.get( 0 ).toString() );
+  }
+  
+  public void testSearchLongestPhrase() throws Exception {
+    make1d1fIndex( "d a b d c a b c" );
+
+    BooleanQuery query = new BooleanQuery();
+    query.add( pqF( "a", "b" ), Occur.SHOULD );
+    query.add( pqF( "a", "b", "c" ), Occur.SHOULD );
+    FieldQuery fq = new FieldQuery( query, true, true );
+    FieldTermStack stack = new FieldTermStack( reader, 0, F, fq );
+    FieldPhraseList fpl = new FieldPhraseList( stack, fq );
+    assertEquals( 2, fpl.phraseList.size() );
+    assertEquals( "ab(1.0)((2,5))", fpl.phraseList.get( 0 ).toString() );
+    assertEquals( "abc(1.0)((10,15))", fpl.phraseList.get( 1 ).toString() );
+  }
+  
+  public void test1PhraseShortMV() throws Exception {
+    makeIndexShortMV();
+
+    FieldQuery fq = new FieldQuery( tq( "d" ), true, true );
+    FieldTermStack stack = new FieldTermStack( reader, 0, F, fq );
+    FieldPhraseList fpl = new FieldPhraseList( stack, fq );
+    assertEquals( 1, fpl.phraseList.size() );
+    assertEquals( "d(1.0)((6,7))", fpl.phraseList.get( 0 ).toString() );
+  }
+  
+  public void test1PhraseLongMV() throws Exception {
+    makeIndexLongMV();
+
+    FieldQuery fq = new FieldQuery( pqF( "search", "engines" ), true, true );
+    FieldTermStack stack = new FieldTermStack( reader, 0, F, fq );
+    FieldPhraseList fpl = new FieldPhraseList( stack, fq );
+    assertEquals( 2, fpl.phraseList.size() );
+    assertEquals( "searchengines(1.0)((102,116))", fpl.phraseList.get( 0 ).toString() );
+    assertEquals( "searchengines(1.0)((157,171))", fpl.phraseList.get( 1 ).toString() );
+  }
+/*
+ * ----------------------------------
+ *  THIS TEST DEPENDS ON LUCENE-1448
+ *  UNCOMMENT WHEN IT IS COMMITTED.
+ * ----------------------------------
+  public void test1PhraseLongMVB() throws Exception {
+    makeIndexLongMVB();
+
+    FieldQuery fq = new FieldQuery( pqF( "sp", "pe", "ee", "ed" ), true, true ); // "speed" -(2gram)-> "sp","pe","ee","ed"
+    FieldTermStack stack = new FieldTermStack( reader, 0, F, fq );
+    FieldPhraseList fpl = new FieldPhraseList( stack, fq );
+    assertEquals( 1, fpl.phraseList.size() );
+    assertEquals( "sppeeeed(1.0)((88,93))", fpl.phraseList.get( 0 ).toString() );
+  }
+*/
+}

Property changes on: contrib/fast-vector-highlighter/src/test/org/apache/lucene/search/vectorhighlight/FieldPhraseListTest.java
___________________________________________________________________
Name: svn:keywords
   + Date Author Id Revision HeadURL
Name: svn:eol-style
   + native

Index: contrib/fast-vector-highlighter/src/test/org/apache/lucene/search/vectorhighlight/IndexTimeSynonymTest.java
===================================================================
--- contrib/fast-vector-highlighter/src/test/org/apache/lucene/search/vectorhighlight/IndexTimeSynonymTest.java	(revision 0)
+++ contrib/fast-vector-highlighter/src/test/org/apache/lucene/search/vectorhighlight/IndexTimeSynonymTest.java	(revision 0)
@@ -0,0 +1,308 @@
+package org.apache.lucene.search.vectorhighlight;
+
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import java.io.IOException;
+import java.io.Reader;
+import java.util.HashSet;
+import java.util.Set;
+
+import org.apache.lucene.analysis.Analyzer;
+import org.apache.lucene.analysis.Token;
+import org.apache.lucene.analysis.TokenStream;
+import org.apache.lucene.search.BooleanQuery;
+import org.apache.lucene.search.BooleanClause.Occur;
+
+public class IndexTimeSynonymTest extends AbstractTestCase {
+  
+  public void testFieldTermStackIndex1wSearch1term() throws Exception {
+    makeIndex1w();
+    
+    FieldQuery fq = new FieldQuery( tq( "Mac" ), true, true );
+    FieldTermStack stack = new FieldTermStack( reader, 0, F, fq );
+    assertEquals( 1, stack.termList.size() );
+    assertEquals( "Mac(11,20,3)", stack.pop().toString() );
+  }
+  
+  public void testFieldTermStackIndex1wSearch2terms() throws Exception {
+    makeIndex1w();
+
+    BooleanQuery bq = new BooleanQuery();
+    bq.add( tq( "Mac" ), Occur.SHOULD );
+    bq.add( tq( "MacBook" ), Occur.SHOULD );
+    FieldQuery fq = new FieldQuery( bq, true, true );
+    FieldTermStack stack = new FieldTermStack( reader, 0, F, fq );
+    assertEquals( 2, stack.termList.size() );
+    Set<String> expectedSet = new HashSet<String>();
+    expectedSet.add( "Mac(11,20,3)" );
+    expectedSet.add( "MacBook(11,20,3)" );
+    assertTrue( expectedSet.contains( stack.pop().toString() ) );
+    assertTrue( expectedSet.contains( stack.pop().toString() ) );
+  }
+  
+  public void testFieldTermStackIndex1w2wSearch1term() throws Exception {
+    makeIndex1w2w();
+    
+    FieldQuery fq = new FieldQuery( tq( "pc" ), true, true );
+    FieldTermStack stack = new FieldTermStack( reader, 0, F, fq );
+    assertEquals( 1, stack.termList.size() );
+    assertEquals( "pc(3,5,1)", stack.pop().toString() );
+  }
+  
+  public void testFieldTermStackIndex1w2wSearch1phrase() throws Exception {
+    makeIndex1w2w();
+    
+    FieldQuery fq = new FieldQuery( pqF( "personal", "computer" ), true, true );
+    FieldTermStack stack = new FieldTermStack( reader, 0, F, fq );
+    assertEquals( 2, stack.termList.size() );
+    assertEquals( "personal(3,5,1)", stack.pop().toString() );
+    assertEquals( "computer(3,5,2)", stack.pop().toString() );
+  }
+  
+  public void testFieldTermStackIndex1w2wSearch1partial() throws Exception {
+    makeIndex1w2w();
+    
+    FieldQuery fq = new FieldQuery( tq( "computer" ), true, true );
+    FieldTermStack stack = new FieldTermStack( reader, 0, F, fq );
+    assertEquals( 1, stack.termList.size() );
+    assertEquals( "computer(3,5,2)", stack.pop().toString() );
+  }
+  
+  public void testFieldTermStackIndex1w2wSearch1term1phrase() throws Exception {
+    makeIndex1w2w();
+
+    BooleanQuery bq = new BooleanQuery();
+    bq.add( tq( "pc" ), Occur.SHOULD );
+    bq.add( pqF( "personal", "computer" ), Occur.SHOULD );
+    FieldQuery fq = new FieldQuery( bq, true, true );
+    FieldTermStack stack = new FieldTermStack( reader, 0, F, fq );
+    assertEquals( 3, stack.termList.size() );
+    Set<String> expectedSet = new HashSet<String>();
+    expectedSet.add( "pc(3,5,1)" );
+    expectedSet.add( "personal(3,5,1)" );
+    assertTrue( expectedSet.contains( stack.pop().toString() ) );
+    assertTrue( expectedSet.contains( stack.pop().toString() ) );
+    assertEquals( "computer(3,5,2)", stack.pop().toString() );
+  }
+  
+  public void testFieldTermStackIndex2w1wSearch1term() throws Exception {
+    makeIndex2w1w();
+    
+    FieldQuery fq = new FieldQuery( tq( "pc" ), true, true );
+    FieldTermStack stack = new FieldTermStack( reader, 0, F, fq );
+    assertEquals( 1, stack.termList.size() );
+    assertEquals( "pc(3,20,1)", stack.pop().toString() );
+  }
+  
+  public void testFieldTermStackIndex2w1wSearch1phrase() throws Exception {
+    makeIndex2w1w();
+    
+    FieldQuery fq = new FieldQuery( pqF( "personal", "computer" ), true, true );
+    FieldTermStack stack = new FieldTermStack( reader, 0, F, fq );
+    assertEquals( 2, stack.termList.size() );
+    assertEquals( "personal(3,20,1)", stack.pop().toString() );
+    assertEquals( "computer(3,20,2)", stack.pop().toString() );
+  }
+  
+  public void testFieldTermStackIndex2w1wSearch1partial() throws Exception {
+    makeIndex2w1w();
+    
+    FieldQuery fq = new FieldQuery( tq( "computer" ), true, true );
+    FieldTermStack stack = new FieldTermStack( reader, 0, F, fq );
+    assertEquals( 1, stack.termList.size() );
+    assertEquals( "computer(3,20,2)", stack.pop().toString() );
+  }
+  
+  public void testFieldTermStackIndex2w1wSearch1term1phrase() throws Exception {
+    makeIndex2w1w();
+
+    BooleanQuery bq = new BooleanQuery();
+    bq.add( tq( "pc" ), Occur.SHOULD );
+    bq.add( pqF( "personal", "computer" ), Occur.SHOULD );
+    FieldQuery fq = new FieldQuery( bq, true, true );
+    FieldTermStack stack = new FieldTermStack( reader, 0, F, fq );
+    assertEquals( 3, stack.termList.size() );
+    Set<String> expectedSet = new HashSet<String>();
+    expectedSet.add( "pc(3,20,1)" );
+    expectedSet.add( "personal(3,20,1)" );
+    assertTrue( expectedSet.contains( stack.pop().toString() ) );
+    assertTrue( expectedSet.contains( stack.pop().toString() ) );
+    assertEquals( "computer(3,20,2)", stack.pop().toString() );
+  }
+  
+  public void testFieldPhraseListIndex1w2wSearch1phrase() throws Exception {
+    makeIndex1w2w();
+    
+    FieldQuery fq = new FieldQuery( pqF( "personal", "computer" ), true, true );
+    FieldTermStack stack = new FieldTermStack( reader, 0, F, fq );
+    FieldPhraseList fpl = new FieldPhraseList( stack, fq );
+    assertEquals( 1, fpl.phraseList.size() );
+    assertEquals( "personalcomputer(1.0)((3,5))", fpl.phraseList.get( 0 ).toString() );
+    assertEquals( 3, fpl.phraseList.get( 0 ).getStartOffset() );
+    assertEquals( 5, fpl.phraseList.get( 0 ).getEndOffset() );
+  }
+  
+  public void testFieldPhraseListIndex1w2wSearch1partial() throws Exception {
+    makeIndex1w2w();
+    
+    FieldQuery fq = new FieldQuery( tq( "computer" ), true, true );
+    FieldTermStack stack = new FieldTermStack( reader, 0, F, fq );
+    FieldPhraseList fpl = new FieldPhraseList( stack, fq );
+    assertEquals( 1, fpl.phraseList.size() );
+    assertEquals( "computer(1.0)((3,5))", fpl.phraseList.get( 0 ).toString() );
+    assertEquals( 3, fpl.phraseList.get( 0 ).getStartOffset() );
+    assertEquals( 5, fpl.phraseList.get( 0 ).getEndOffset() );
+  }
+  
+  public void testFieldPhraseListIndex1w2wSearch1term1phrase() throws Exception {
+    makeIndex1w2w();
+
+    BooleanQuery bq = new BooleanQuery();
+    bq.add( tq( "pc" ), Occur.SHOULD );
+    bq.add( pqF( "personal", "computer" ), Occur.SHOULD );
+    FieldQuery fq = new FieldQuery( bq, true, true );
+    FieldTermStack stack = new FieldTermStack( reader, 0, F, fq );
+    FieldPhraseList fpl = new FieldPhraseList( stack, fq );
+    assertEquals( 1, fpl.phraseList.size() );
+    assertTrue( fpl.phraseList.get( 0 ).toString().indexOf( "(1.0)((3,5))" ) > 0 );
+    assertEquals( 3, fpl.phraseList.get( 0 ).getStartOffset() );
+    assertEquals( 5, fpl.phraseList.get( 0 ).getEndOffset() );
+  }
+  
+  public void testFieldPhraseListIndex2w1wSearch1term() throws Exception {
+    makeIndex2w1w();
+    
+    FieldQuery fq = new FieldQuery( tq( "pc" ), true, true );
+    FieldTermStack stack = new FieldTermStack( reader, 0, F, fq );
+    FieldPhraseList fpl = new FieldPhraseList( stack, fq );
+    assertEquals( 1, fpl.phraseList.size() );
+    assertEquals( "pc(1.0)((3,20))", fpl.phraseList.get( 0 ).toString() );
+    assertEquals( 3, fpl.phraseList.get( 0 ).getStartOffset() );
+    assertEquals( 20, fpl.phraseList.get( 0 ).getEndOffset() );
+  }
+  
+  public void testFieldPhraseListIndex2w1wSearch1phrase() throws Exception {
+    makeIndex2w1w();
+    
+    FieldQuery fq = new FieldQuery( pqF( "personal", "computer" ), true, true );
+    FieldTermStack stack = new FieldTermStack( reader, 0, F, fq );
+    FieldPhraseList fpl = new FieldPhraseList( stack, fq );
+    assertEquals( 1, fpl.phraseList.size() );
+    assertEquals( "personalcomputer(1.0)((3,20))", fpl.phraseList.get( 0 ).toString() );
+    assertEquals( 3, fpl.phraseList.get( 0 ).getStartOffset() );
+    assertEquals( 20, fpl.phraseList.get( 0 ).getEndOffset() );
+  }
+  
+  public void testFieldPhraseListIndex2w1wSearch1partial() throws Exception {
+    makeIndex2w1w();
+    
+    FieldQuery fq = new FieldQuery( tq( "computer" ), true, true );
+    FieldTermStack stack = new FieldTermStack( reader, 0, F, fq );
+    FieldPhraseList fpl = new FieldPhraseList( stack, fq );
+    assertEquals( 1, fpl.phraseList.size() );
+    assertEquals( "computer(1.0)((3,20))", fpl.phraseList.get( 0 ).toString() );
+    assertEquals( 3, fpl.phraseList.get( 0 ).getStartOffset() );
+    assertEquals( 20, fpl.phraseList.get( 0 ).getEndOffset() );
+  }
+  
+  public void testFieldPhraseListIndex2w1wSearch1term1phrase() throws Exception {
+    makeIndex2w1w();
+
+    BooleanQuery bq = new BooleanQuery();
+    bq.add( tq( "pc" ), Occur.SHOULD );
+    bq.add( pqF( "personal", "computer" ), Occur.SHOULD );
+    FieldQuery fq = new FieldQuery( bq, true, true );
+    FieldTermStack stack = new FieldTermStack( reader, 0, F, fq );
+    FieldPhraseList fpl = new FieldPhraseList( stack, fq );
+    assertEquals( 1, fpl.phraseList.size() );
+    assertTrue( fpl.phraseList.get( 0 ).toString().indexOf( "(1.0)((3,20))" ) > 0 );
+    assertEquals( 3, fpl.phraseList.get( 0 ).getStartOffset() );
+    assertEquals( 20, fpl.phraseList.get( 0 ).getEndOffset() );
+  }
+
+  private void makeIndex1w() throws Exception {
+    //           11111111112
+    // 012345678901234567890
+    // I'll buy a Macintosh
+    //            Mac
+    //            MacBook
+    // 0    1   2 3
+    makeSynonymIndex( "I'll buy a Macintosh",
+        t("I'll",0,4),
+        t("buy",5,8),
+        t("a",9,10),
+        t("Macintosh",11,20),t("Mac",11,20,0),t("MacBook",11,20,0));
+  }
+
+  private void makeIndex1w2w() throws Exception {
+    //           1111111
+    // 01234567890123456
+    // My pc was broken
+    //    personal computer
+    // 0  1  2   3
+    makeSynonymIndex( "My pc was broken",
+        t("My",0,2),
+        t("pc",3,5),t("personal",3,5,0),t("computer",3,5),
+        t("was",6,9),
+        t("broken",10,16));
+  }
+
+  private void makeIndex2w1w() throws Exception {
+    //           1111111111222222222233
+    // 01234567890123456789012345678901
+    // My personal computer was broken
+    //    pc
+    // 0  1        2        3   4
+    makeSynonymIndex( "My personal computer was broken",
+        t("My",0,2),
+        t("personal",3,20),t("pc",3,20,0),t("computer",3,20),
+        t("was",21,24),
+        t("broken",25,31));
+  }
+  
+  void makeSynonymIndex( String value, Token... tokens ) throws Exception {
+    Analyzer analyzer = new TokenArrayAnalyzer( tokens );
+    make1dmfIndex( analyzer, value );
+  }
+
+  public static Token t( String text, int startOffset, int endOffset ){
+    return t( text, startOffset, endOffset, 1 );
+  }
+  
+  public static Token t( String text, int startOffset, int endOffset, int positionIncrement ){
+    Token token = new Token( text, startOffset, endOffset );
+    token.setPositionIncrement( positionIncrement );
+    return token;
+  }
+  
+  public static class TokenArrayAnalyzer extends Analyzer {
+    Token[] tokens;
+    public TokenArrayAnalyzer( Token... tokens ){
+      this.tokens = tokens;
+    }
+    public TokenStream tokenStream(String fieldName, Reader reader) {
+      return new TokenStream(){
+        int p = 0;
+        public Token next( Token reusableToken ) throws IOException {
+          if( p >= tokens.length ) return null;
+          return tokens[p++];
+        }
+      };
+    }
+  }
+}

Property changes on: contrib/fast-vector-highlighter/src/test/org/apache/lucene/search/vectorhighlight/IndexTimeSynonymTest.java
___________________________________________________________________
Name: svn:keywords
   + Date Author Id Revision HeadURL
Name: svn:eol-style
   + native

Index: contrib/fast-vector-highlighter/src/test/org/apache/lucene/search/vectorhighlight/ScoreOrderFragmentsBuilderTest.java
===================================================================
--- contrib/fast-vector-highlighter/src/test/org/apache/lucene/search/vectorhighlight/ScoreOrderFragmentsBuilderTest.java	(revision 0)
+++ contrib/fast-vector-highlighter/src/test/org/apache/lucene/search/vectorhighlight/ScoreOrderFragmentsBuilderTest.java	(revision 0)
@@ -0,0 +1,43 @@
+package org.apache.lucene.search.vectorhighlight;
+
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import org.apache.lucene.search.Query;
+
+public class ScoreOrderFragmentsBuilderTest extends AbstractTestCase {
+  
+  public void test3Frags() throws Exception {
+    FieldFragList ffl = ffl( "a c", "a b b b b b b b b b b b a b a b b b b b c a a b b" );
+    ScoreOrderFragmentsBuilder sofb = new ScoreOrderFragmentsBuilder();
+    String[] f = sofb.createFragments( reader, 0, F, ffl, 3 );
+    assertEquals( 3, f.length );
+    // check score order
+    assertEquals( "<b>c</b> <b>a</b> <b>a</b> b b", f[0] );
+    assertEquals( "b b <b>a</b> b <b>a</b> b b b b b ", f[1] );
+    assertEquals( "<b>a</b> b b b b b b b b b ", f[2] );
+  }
+
+  private FieldFragList ffl( String queryValue, String indexValue ) throws Exception {
+    make1d1fIndex( indexValue );
+    Query query = paW.parse( queryValue );
+    FieldQuery fq = new FieldQuery( query, true, true );
+    FieldTermStack stack = new FieldTermStack( reader, 0, F, fq );
+    FieldPhraseList fpl = new FieldPhraseList( stack, fq );
+    return new SimpleFragListBuilder().createFieldFragList( fpl, 20 );
+  }
+}

Property changes on: contrib/fast-vector-highlighter/src/test/org/apache/lucene/search/vectorhighlight/ScoreOrderFragmentsBuilderTest.java
___________________________________________________________________
Name: svn:keywords
   + Date Author Id Revision HeadURL
Name: svn:eol-style
   + native

Index: contrib/fast-vector-highlighter/src/test/org/apache/lucene/search/vectorhighlight/SimpleFragmentsBuilderTest.java
===================================================================
--- contrib/fast-vector-highlighter/src/test/org/apache/lucene/search/vectorhighlight/SimpleFragmentsBuilderTest.java	(revision 0)
+++ contrib/fast-vector-highlighter/src/test/org/apache/lucene/search/vectorhighlight/SimpleFragmentsBuilderTest.java	(revision 0)
@@ -0,0 +1,104 @@
+package org.apache.lucene.search.vectorhighlight;
+
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import org.apache.lucene.search.Query;
+
+public class SimpleFragmentsBuilderTest extends AbstractTestCase {
+  
+  public void test1TermIndex() throws Exception {
+    FieldFragList ffl = ffl( "a", "a" );
+    SimpleFragmentsBuilder sfb = new SimpleFragmentsBuilder();
+    assertEquals( "<b>a</b>", sfb.createFragment( reader, 0, F, ffl ) );
+
+    // change tags
+    sfb = new SimpleFragmentsBuilder( new String[]{ "[" }, new String[]{ "]" } );
+    assertEquals( "[a]", sfb.createFragment( reader, 0, F, ffl ) );
+  }
+  
+  public void test2Frags() throws Exception {
+    FieldFragList ffl = ffl( "a", "a b b b b b b b b b b b a b a b" );
+    SimpleFragmentsBuilder sfb = new SimpleFragmentsBuilder();
+    String[] f = sfb.createFragments( reader, 0, F, ffl, 3 );
+    // 3 snippets requested, but should be 2
+    assertEquals( 2, f.length );
+    assertEquals( "<b>a</b> b b b b b b b b b ", f[0] );
+    assertEquals( "b b <b>a</b> b <b>a</b> b", f[1] );
+  }
+  
+  public void test3Frags() throws Exception {
+    FieldFragList ffl = ffl( "a c", "a b b b b b b b b b b b a b a b b b b b c a a b b" );
+    SimpleFragmentsBuilder sfb = new SimpleFragmentsBuilder();
+    String[] f = sfb.createFragments( reader, 0, F, ffl, 3 );
+    assertEquals( 3, f.length );
+    assertEquals( "<b>a</b> b b b b b b b b b ", f[0] );
+    assertEquals( "b b <b>a</b> b <b>a</b> b b b b b ", f[1] );
+    assertEquals( "<b>c</b> <b>a</b> <b>a</b> b b", f[2] );
+  }
+
+  private FieldFragList ffl( String queryValue, String indexValue ) throws Exception {
+    make1d1fIndex( indexValue );
+    Query query = paW.parse( queryValue );
+    FieldQuery fq = new FieldQuery( query, true, true );
+    FieldTermStack stack = new FieldTermStack( reader, 0, F, fq );
+    FieldPhraseList fpl = new FieldPhraseList( stack, fq );
+    return new SimpleFragListBuilder().createFieldFragList( fpl, 20 );
+  }
+  
+  public void test1PhraseShortMV() throws Exception {
+    makeIndexShortMV();
+
+    FieldQuery fq = new FieldQuery( tq( "d" ), true, true );
+    FieldTermStack stack = new FieldTermStack( reader, 0, F, fq );
+    FieldPhraseList fpl = new FieldPhraseList( stack, fq );
+    SimpleFragListBuilder sflb = new SimpleFragListBuilder();
+    FieldFragList ffl = sflb.createFieldFragList( fpl, 100 );
+    SimpleFragmentsBuilder sfb = new SimpleFragmentsBuilder();
+    assertEquals( "a b c <b>d</b> e", sfb.createFragment( reader, 0, F, ffl ) );
+  }
+  
+  public void test1PhraseLongMV() throws Exception {
+    makeIndexLongMV();
+
+    FieldQuery fq = new FieldQuery( pqF( "search", "engines" ), true, true );
+    FieldTermStack stack = new FieldTermStack( reader, 0, F, fq );
+    FieldPhraseList fpl = new FieldPhraseList( stack, fq );
+    SimpleFragListBuilder sflb = new SimpleFragListBuilder();
+    FieldFragList ffl = sflb.createFieldFragList( fpl, 100 );
+    SimpleFragmentsBuilder sfb = new SimpleFragmentsBuilder();
+    assertEquals( " most <b>search engines</b> use only one of these methods. Even the <b>search engines</b> that says they can use t",
+        sfb.createFragment( reader, 0, F, ffl ) );
+  }
+/*
+ * ----------------------------------
+ *  THIS TEST DEPENDS ON LUCENE-1448
+ *  UNCOMMENT WHEN IT IS COMMITTED.
+ * ----------------------------------
+  public void test1PhraseLongMVB() throws Exception {
+    makeIndexLongMVB();
+
+    FieldQuery fq = new FieldQuery( pqF( "sp", "pe", "ee", "ed" ), true, true ); // "speed" -(2gram)-> "sp","pe","ee","ed"
+    FieldTermStack stack = new FieldTermStack( reader, 0, F, fq );
+    FieldPhraseList fpl = new FieldPhraseList( stack, fq );
+    SimpleFragListBuilder sflb = new SimpleFragListBuilder();
+    FieldFragList ffl = sflb.createFieldFragList( fpl, 100 );
+    SimpleFragmentsBuilder sfb = new SimpleFragmentsBuilder();
+    assertEquals( "ssing <b>speed</b>, the", sfb.createFragment( reader, 0, F, ffl ) );
+  }
+*/
+}

Property changes on: contrib/fast-vector-highlighter/src/test/org/apache/lucene/search/vectorhighlight/SimpleFragmentsBuilderTest.java
___________________________________________________________________
Name: svn:keywords
   + Date Author Id Revision HeadURL
Name: svn:eol-style
   + native

Index: contrib/fast-vector-highlighter/src/test/org/apache/lucene/search/vectorhighlight/FieldQueryTest.java
===================================================================
--- contrib/fast-vector-highlighter/src/test/org/apache/lucene/search/vectorhighlight/FieldQueryTest.java	(revision 0)
+++ contrib/fast-vector-highlighter/src/test/org/apache/lucene/search/vectorhighlight/FieldQueryTest.java	(revision 0)
@@ -0,0 +1,822 @@
+package org.apache.lucene.search.vectorhighlight;
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import org.apache.lucene.search.BooleanQuery;
+import org.apache.lucene.search.Query;
+import org.apache.lucene.search.BooleanClause.Occur;
+import org.apache.lucene.search.vectorhighlight.FieldQuery.QueryPhraseMap;
+import org.apache.lucene.search.vectorhighlight.FieldTermStack.TermInfo;
+
+public class FieldQueryTest extends AbstractTestCase {
+
+  public void testFlattenBoolean() throws Exception {
+    Query query = paW.parse( "A AND B OR C NOT (D AND E)" );
+    FieldQuery fq = new FieldQuery( query, true, true );
+    Set<Query> flatQueries = new HashSet<Query>();
+    fq.flatten( query, flatQueries );
+    assertCollectionQueries( flatQueries, tq( "A" ), tq( "B" ), tq( "C" ) );
+  }
+
+  public void testFlattenTermAndPhrase() throws Exception {
+    Query query = paW.parse( "A AND \"B C\"" );
+    FieldQuery fq = new FieldQuery( query, true, true );
+    Set<Query> flatQueries = new HashSet<Query>();
+    fq.flatten( query, flatQueries );
+    assertCollectionQueries( flatQueries, tq( "A" ), pqF( "B", "C" ) );
+  }
+
+  public void testFlattenTermAndPhrase2gram() throws Exception {
+    Query query = paB.parse( "AA AND BCD OR EFGH" );
+    FieldQuery fq = new FieldQuery( query, true, true );
+    Set<Query> flatQueries = new HashSet<Query>();
+    fq.flatten( query, flatQueries );
+    assertCollectionQueries( flatQueries, tq( "AA" ), pqF( "BC", "CD" ), pqF( "EF", "FG", "GH" ) );
+  }
+
+  public void testFlatten1TermPhrase() throws Exception {
+    Query query = pqF( "A" );
+    FieldQuery fq = new FieldQuery( query, true, true );
+    Set<Query> flatQueries = new HashSet<Query>();
+    fq.flatten( query, flatQueries );
+    assertCollectionQueries( flatQueries, tq( "A" ) );
+  }
+
+  public void testExpand() throws Exception {
+    Query dummy = pqF( "DUMMY" );
+    FieldQuery fq = new FieldQuery( dummy, true, true );
+
+    // "a b","b c" => "a b","b c","a b c"
+    Set<Query> flatQueries = new HashSet<Query>();
+    flatQueries.add( pqF( "a", "b" ) );
+    flatQueries.add( pqF( "b", "c" ) );
+    assertCollectionQueries( fq.expand( flatQueries ),
+        pqF( "a", "b" ), pqF( "b", "c" ), pqF( "a", "b", "c" ) );
+
+    // "a b","b c d" => "a b","b c d","a b c d"
+    flatQueries = new HashSet<Query>();
+    flatQueries.add( pqF( "a", "b" ) );
+    flatQueries.add( pqF( "b", "c", "d" ) );
+    assertCollectionQueries( fq.expand( flatQueries ),
+        pqF( "a", "b" ), pqF( "b", "c", "d" ), pqF( "a", "b", "c", "d" ) );
+
+    // "a b c","b c d" => "a b c","b c d","a b c d"
+    flatQueries = new HashSet<Query>();
+    flatQueries.add( pqF( "a", "b", "c" ) );
+    flatQueries.add( pqF( "b", "c", "d" ) );
+    assertCollectionQueries( fq.expand( flatQueries ),
+        pqF( "a", "b", "c" ), pqF( "b", "c", "d" ), pqF( "a", "b", "c", "d" ) );
+
+    // "a b c","c d e" => "a b c","c d e","a b c d e"
+    flatQueries = new HashSet<Query>();
+    flatQueries.add( pqF( "a", "b", "c" ) );
+    flatQueries.add( pqF( "c", "d", "e" ) );
+    assertCollectionQueries( fq.expand( flatQueries ),
+        pqF( "a", "b", "c" ), pqF( "c", "d", "e" ), pqF( "a", "b", "c", "d", "e" ) );
+
+    // "a b b","b c" => "a b b","b c","a b b c"
+    flatQueries = new HashSet<Query>();
+    flatQueries.add( pqF( "a", "b", "b" ) );
+    flatQueries.add( pqF( "b", "c" ) );
+    assertCollectionQueries( fq.expand( flatQueries ),
+        pqF( "a", "b", "b" ), pqF( "b", "c" ), pqF( "a", "b", "b", "c" ) );
+
+    // "a b","b a" => "a b","b a","a b a", "b a b"
+    flatQueries = new HashSet<Query>();
+    flatQueries.add( pqF( "a", "b" ) );
+    flatQueries.add( pqF( "b", "a" ) );
+    assertCollectionQueries( fq.expand( flatQueries ),
+        pqF( "a", "b" ), pqF( "b", "a" ), pqF( "a", "b", "a" ), pqF( "b", "a", "b" ) );
+
+    // "a b","a b c" => "a b","a b c"
+    flatQueries = new HashSet<Query>();
+    flatQueries.add( pqF( "a", "b" ) );
+    flatQueries.add( pqF( "a", "b", "c" ) );
+    assertCollectionQueries( fq.expand( flatQueries ),
+        pqF( "a", "b" ), pqF( "a", "b", "c" ) );
+  }
+
+  public void testNoExpand() throws Exception {
+    Query dummy = pqF( "DUMMY" );
+    FieldQuery fq = new FieldQuery( dummy, true, true );
+
+    // "a b","c d" => "a b","c d"
+    Set<Query> flatQueries = new HashSet<Query>();
+    flatQueries.add( pqF( "a", "b" ) );
+    flatQueries.add( pqF( "c", "d" ) );
+    assertCollectionQueries( fq.expand( flatQueries ),
+        pqF( "a", "b" ), pqF( "c", "d" ) );
+
+    // "a","a b" => "a", "a b"
+    flatQueries = new HashSet<Query>();
+    flatQueries.add( tq( "a" ) );
+    flatQueries.add( pqF( "a", "b" ) );
+    assertCollectionQueries( fq.expand( flatQueries ),
+        tq( "a" ), pqF( "a", "b" ) );
+
+    // "a b","b" => "a b", "b"
+    flatQueries = new HashSet<Query>();
+    flatQueries.add( pqF( "a", "b" ) );
+    flatQueries.add( tq( "b" ) );
+    assertCollectionQueries( fq.expand( flatQueries ),
+        pqF( "a", "b" ), tq( "b" ) );
+
+    // "a b c","b c" => "a b c","b c"
+    flatQueries = new HashSet<Query>();
+    flatQueries.add( pqF( "a", "b", "c" ) );
+    flatQueries.add( pqF( "b", "c" ) );
+    assertCollectionQueries( fq.expand( flatQueries ),
+        pqF( "a", "b", "c" ), pqF( "b", "c" ) );
+
+    // "a b","a b c" => "a b","a b c"
+    flatQueries = new HashSet<Query>();
+    flatQueries.add( pqF( "a", "b" ) );
+    flatQueries.add( pqF( "a", "b", "c" ) );
+    assertCollectionQueries( fq.expand( flatQueries ),
+        pqF( "a", "b" ), pqF( "a", "b", "c" ) );
+
+    // "a b c","b d e" => "a b c","b d e"
+    flatQueries = new HashSet<Query>();
+    flatQueries.add( pqF( "a", "b", "c" ) );
+    flatQueries.add( pqF( "b", "d", "e" ) );
+    assertCollectionQueries( fq.expand( flatQueries ),
+        pqF( "a", "b", "c" ), pqF( "b", "d", "e" ) );
+  }
+
+  public void testExpandNotFieldMatch() throws Exception {
+    Query dummy = pqF( "DUMMY" );
+    FieldQuery fq = new FieldQuery( dummy, true, false );
+
+    // f1:"a b",f2:"b c" => f1:"a b",f2:"b c",f1:"a b c"
+    Set<Query> flatQueries = new HashSet<Query>();
+    flatQueries.add( pq( F1, "a", "b" ) );
+    flatQueries.add( pq( F2, "b", "c" ) );
+    assertCollectionQueries( fq.expand( flatQueries ),
+        pq( F1, "a", "b" ), pq( F2, "b", "c" ), pq( F1, "a", "b", "c" ) );
+  }
+
+  public void testGetFieldTermMap() throws Exception {
+    Query query = tq( "a" );
+    FieldQuery fq = new FieldQuery( query, true, true );
+    
+    QueryPhraseMap pqm = fq.getFieldTermMap( F, "a" );
+    assertNotNull( pqm );
+    assertTrue( pqm.isTerminal() );
+    
+    pqm = fq.getFieldTermMap( F, "b" );
+    assertNull( pqm );
+    
+    pqm = fq.getFieldTermMap( F1, "a" );
+    assertNull( pqm );
+  }
+
+  public void testGetRootMap() throws Exception {
+    Query dummy = pqF( "DUMMY" );
+    FieldQuery fq = new FieldQuery( dummy, true, true );
+
+    QueryPhraseMap rootMap1 = fq.getRootMap( tq( "a" ) );
+    QueryPhraseMap rootMap2 = fq.getRootMap( tq( "a" ) );
+    assertTrue( rootMap1 == rootMap2 );
+    QueryPhraseMap rootMap3 = fq.getRootMap( tq( "b" ) );
+    assertTrue( rootMap1 == rootMap3 );
+    QueryPhraseMap rootMap4 = fq.getRootMap( tq( F1, "b" ) );
+    assertFalse( rootMap4 == rootMap3 );
+  }
+
+  public void testGetRootMapNotFieldMatch() throws Exception {
+    Query dummy = pqF( "DUMMY" );
+    FieldQuery fq = new FieldQuery( dummy, true, false );
+
+    QueryPhraseMap rootMap1 = fq.getRootMap( tq( "a" ) );
+    QueryPhraseMap rootMap2 = fq.getRootMap( tq( "a" ) );
+    assertTrue( rootMap1 == rootMap2 );
+    QueryPhraseMap rootMap3 = fq.getRootMap( tq( "b" ) );
+    assertTrue( rootMap1 == rootMap3 );
+    QueryPhraseMap rootMap4 = fq.getRootMap( tq( F1, "b" ) );
+    assertTrue( rootMap4 == rootMap3 );
+  }
+
+  public void testGetTermSet() throws Exception {
+    Query query = paW.parse( "A AND B OR x:C NOT (D AND E)" );
+    FieldQuery fq = new FieldQuery( query, true, true );
+    assertEquals( 2, fq.termSetMap.size() );
+    Set<String> termSet = fq.getTermSet( F );
+    assertEquals( 2, termSet.size() );
+    assertTrue( termSet.contains( "A" ) );
+    assertTrue( termSet.contains( "B" ) );
+    termSet = fq.getTermSet( "x" );
+    assertEquals( 1, termSet.size() );
+    assertTrue( termSet.contains( "C" ) );
+    termSet = fq.getTermSet( "y" );
+    assertNull( termSet );
+  }
+  
+  public void testQueryPhraseMap1Term() throws Exception {
+    Query query = tq( "a" );
+    
+    // phraseHighlight = true, fieldMatch = true
+    FieldQuery fq = new FieldQuery( query, true, true );
+    Map<String, QueryPhraseMap> map = fq.rootMaps;
+    assertEquals( 1, map.size() );
+    assertNull( map.get( null ) );
+    assertNotNull( map.get( F ) );
+    QueryPhraseMap qpm = map.get( F );
+    assertEquals( 1, qpm.subMap.size() );
+    assertTrue( qpm.subMap.get( "a" ) != null );
+    assertTrue( qpm.subMap.get( "a" ).terminal );
+    assertEquals( 1F, qpm.subMap.get( "a" ).boost );
+    
+    // phraseHighlight = true, fieldMatch = false
+    fq = new FieldQuery( query, true, false );
+    map = fq.rootMaps;
+    assertEquals( 1, map.size() );
+    assertNull( map.get( F ) );
+    assertNotNull( map.get( null ) );
+    qpm = map.get( null );
+    assertEquals( 1, qpm.subMap.size() );
+    assertTrue( qpm.subMap.get( "a" ) != null );
+    assertTrue( qpm.subMap.get( "a" ).terminal );
+    assertEquals( 1F, qpm.subMap.get( "a" ).boost );
+    
+    // phraseHighlight = false, fieldMatch = true
+    fq = new FieldQuery( query, false, true );
+    map = fq.rootMaps;
+    assertEquals( 1, map.size() );
+    assertNull( map.get( null ) );
+    assertNotNull( map.get( F ) );
+    qpm = map.get( F );
+    assertEquals( 1, qpm.subMap.size() );
+    assertTrue( qpm.subMap.get( "a" ) != null );
+    assertTrue( qpm.subMap.get( "a" ).terminal );
+    assertEquals( 1F, qpm.subMap.get( "a" ).boost );
+    
+    // phraseHighlight = false, fieldMatch = false
+    fq = new FieldQuery( query, false, false );
+    map = fq.rootMaps;
+    assertEquals( 1, map.size() );
+    assertNull( map.get( F ) );
+    assertNotNull( map.get( null ) );
+    qpm = map.get( null );
+    assertEquals( 1, qpm.subMap.size() );
+    assertTrue( qpm.subMap.get( "a" ) != null );
+    assertTrue( qpm.subMap.get( "a" ).terminal );
+    assertEquals( 1F, qpm.subMap.get( "a" ).boost );
+    
+    // boost != 1
+    query = tq( 2, "a" );
+    fq = new FieldQuery( query, true, true );
+    map = fq.rootMaps;
+    qpm = map.get( F );
+    assertEquals( 2F, qpm.subMap.get( "a" ).boost );
+  }
+  
+  public void testQueryPhraseMap1Phrase() throws Exception {
+    Query query = pqF( "a", "b" );
+    
+    // phraseHighlight = true, fieldMatch = true
+    FieldQuery fq = new FieldQuery( query, true, true );
+    Map<String, QueryPhraseMap> map = fq.rootMaps;
+    assertEquals( 1, map.size() );
+    assertNull( map.get( null ) );
+    assertNotNull( map.get( F ) );
+    QueryPhraseMap qpm = map.get( F );
+    assertEquals( 1, qpm.subMap.size() );
+    assertNotNull( qpm.subMap.get( "a" ) );
+    QueryPhraseMap qpm2 = qpm.subMap.get( "a" );
+    assertFalse( qpm2.terminal );
+    assertEquals( 1, qpm2.subMap.size() );
+    assertNotNull( qpm2.subMap.get( "b" ) );
+    QueryPhraseMap qpm3 = qpm2.subMap.get( "b" );
+    assertTrue( qpm3.terminal );
+    assertEquals( 1F, qpm3.boost );
+    
+    // phraseHighlight = true, fieldMatch = false
+    fq = new FieldQuery( query, true, false );
+    map = fq.rootMaps;
+    assertEquals( 1, map.size() );
+    assertNull( map.get( F ) );
+    assertNotNull( map.get( null ) );
+    qpm = map.get( null );
+    assertEquals( 1, qpm.subMap.size() );
+    assertNotNull( qpm.subMap.get( "a" ) );
+    qpm2 = qpm.subMap.get( "a" );
+    assertFalse( qpm2.terminal );
+    assertEquals( 1, qpm2.subMap.size() );
+    assertNotNull( qpm2.subMap.get( "b" ) );
+    qpm3 = qpm2.subMap.get( "b" );
+    assertTrue( qpm3.terminal );
+    assertEquals( 1F, qpm3.boost );
+    
+    // phraseHighlight = false, fieldMatch = true
+    fq = new FieldQuery( query, false, true );
+    map = fq.rootMaps;
+    assertEquals( 1, map.size() );
+    assertNull( map.get( null ) );
+    assertNotNull( map.get( F ) );
+    qpm = map.get( F );
+    assertEquals( 2, qpm.subMap.size() );
+    assertNotNull( qpm.subMap.get( "a" ) );
+    qpm2 = qpm.subMap.get( "a" );
+    assertTrue( qpm2.terminal );
+    assertEquals( 1F, qpm2.boost );
+    assertEquals( 1, qpm2.subMap.size() );
+    assertNotNull( qpm2.subMap.get( "b" ) );
+    qpm3 = qpm2.subMap.get( "b" );
+    assertTrue( qpm3.terminal );
+    assertEquals( 1F, qpm3.boost );
+
+    assertNotNull( qpm.subMap.get( "b" ) );
+    qpm2 = qpm.subMap.get( "b" );
+    assertTrue( qpm2.terminal );
+    assertEquals( 1F, qpm2.boost );
+    
+    // phraseHighlight = false, fieldMatch = false
+    fq = new FieldQuery( query, false, false );
+    map = fq.rootMaps;
+    assertEquals( 1, map.size() );
+    assertNull( map.get( F ) );
+    assertNotNull( map.get( null ) );
+    qpm = map.get( null );
+    assertEquals( 2, qpm.subMap.size() );
+    assertNotNull( qpm.subMap.get( "a" ) );
+    qpm2 = qpm.subMap.get( "a" );
+    assertTrue( qpm2.terminal );
+    assertEquals( 1F, qpm2.boost );
+    assertEquals( 1, qpm2.subMap.size() );
+    assertNotNull( qpm2.subMap.get( "b" ) );
+    qpm3 = qpm2.subMap.get( "b" );
+    assertTrue( qpm3.terminal );
+    assertEquals( 1F, qpm3.boost );
+
+    assertNotNull( qpm.subMap.get( "b" ) );
+    qpm2 = qpm.subMap.get( "b" );
+    assertTrue( qpm2.terminal );
+    assertEquals( 1F, qpm2.boost );
+
+    // boost != 1
+    query = pqF( 2, "a", "b" );
+    // phraseHighlight = false, fieldMatch = false
+    fq = new FieldQuery( query, false, false );
+    map = fq.rootMaps;
+    qpm = map.get( null );
+    qpm2 = qpm.subMap.get( "a" );
+    assertEquals( 2F, qpm2.boost );
+    qpm3 = qpm2.subMap.get( "b" );
+    assertEquals( 2F, qpm3.boost );
+    qpm2 = qpm.subMap.get( "b" );
+    assertEquals( 2F, qpm2.boost );
+  }
+  
+  public void testQueryPhraseMap1PhraseAnother() throws Exception {
+    Query query = pqF( "search", "engines" );
+    
+    // phraseHighlight = true, fieldMatch = true
+    FieldQuery fq = new FieldQuery( query, true, true );
+    Map<String, QueryPhraseMap> map = fq.rootMaps;
+    assertEquals( 1, map.size() );
+    assertNull( map.get( null ) );
+    assertNotNull( map.get( F ) );
+    QueryPhraseMap qpm = map.get( F );
+    assertEquals( 1, qpm.subMap.size() );
+    assertNotNull( qpm.subMap.get( "search" ) );
+    QueryPhraseMap qpm2 = qpm.subMap.get( "search" );
+    assertFalse( qpm2.terminal );
+    assertEquals( 1, qpm2.subMap.size() );
+    assertNotNull( qpm2.subMap.get( "engines" ) );
+    QueryPhraseMap qpm3 = qpm2.subMap.get( "engines" );
+    assertTrue( qpm3.terminal );
+    assertEquals( 1F, qpm3.boost );
+  }
+  
+  public void testQueryPhraseMap2Phrases() throws Exception {
+    BooleanQuery query = new BooleanQuery();
+    query.add( pqF( "a", "b" ), Occur.SHOULD );
+    query.add( pqF( 2, "c", "d" ), Occur.SHOULD );
+    
+    // phraseHighlight = true, fieldMatch = true
+    FieldQuery fq = new FieldQuery( query, true, true );
+    Map<String, QueryPhraseMap> map = fq.rootMaps;
+    assertEquals( 1, map.size() );
+    assertNull( map.get( null ) );
+    assertNotNull( map.get( F ) );
+    QueryPhraseMap qpm = map.get( F );
+    assertEquals( 2, qpm.subMap.size() );
+
+    // "a b"
+    assertNotNull( qpm.subMap.get( "a" ) );
+    QueryPhraseMap qpm2 = qpm.subMap.get( "a" );
+    assertFalse( qpm2.terminal );
+    assertEquals( 1, qpm2.subMap.size() );
+    assertNotNull( qpm2.subMap.get( "b" ) );
+    QueryPhraseMap qpm3 = qpm2.subMap.get( "b" );
+    assertTrue( qpm3.terminal );
+    assertEquals( 1F, qpm3.boost );
+
+    // "c d"^2
+    assertNotNull( qpm.subMap.get( "c" ) );
+    qpm2 = qpm.subMap.get( "c" );
+    assertFalse( qpm2.terminal );
+    assertEquals( 1, qpm2.subMap.size() );
+    assertNotNull( qpm2.subMap.get( "d" ) );
+    qpm3 = qpm2.subMap.get( "d" );
+    assertTrue( qpm3.terminal );
+    assertEquals( 2F, qpm3.boost );
+  }
+  
+  public void testQueryPhraseMap2PhrasesFields() throws Exception {
+    BooleanQuery query = new BooleanQuery();
+    query.add( pq( F1, "a", "b" ), Occur.SHOULD );
+    query.add( pq( 2F, F2, "c", "d" ), Occur.SHOULD );
+    
+    // phraseHighlight = true, fieldMatch = true
+    FieldQuery fq = new FieldQuery( query, true, true );
+    Map<String, QueryPhraseMap> map = fq.rootMaps;
+    assertEquals( 2, map.size() );
+    assertNull( map.get( null ) );
+
+    // "a b"
+    assertNotNull( map.get( F1 ) );
+    QueryPhraseMap qpm = map.get( F1 );
+    assertEquals( 1, qpm.subMap.size() );
+    assertNotNull( qpm.subMap.get( "a" ) );
+    QueryPhraseMap qpm2 = qpm.subMap.get( "a" );
+    assertFalse( qpm2.terminal );
+    assertEquals( 1, qpm2.subMap.size() );
+    assertNotNull( qpm2.subMap.get( "b" ) );
+    QueryPhraseMap qpm3 = qpm2.subMap.get( "b" );
+    assertTrue( qpm3.terminal );
+    assertEquals( 1F, qpm3.boost );
+
+    // "c d"^2
+    assertNotNull( map.get( F2 ) );
+    qpm = map.get( F2 );
+    assertEquals( 1, qpm.subMap.size() );
+    assertNotNull( qpm.subMap.get( "c" ) );
+    qpm2 = qpm.subMap.get( "c" );
+    assertFalse( qpm2.terminal );
+    assertEquals( 1, qpm2.subMap.size() );
+    assertNotNull( qpm2.subMap.get( "d" ) );
+    qpm3 = qpm2.subMap.get( "d" );
+    assertTrue( qpm3.terminal );
+    assertEquals( 2F, qpm3.boost );
+    
+    // phraseHighlight = true, fieldMatch = false
+    fq = new FieldQuery( query, true, false );
+    map = fq.rootMaps;
+    assertEquals( 1, map.size() );
+    assertNull( map.get( F1 ) );
+    assertNull( map.get( F2 ) );
+    assertNotNull( map.get( null ) );
+    qpm = map.get( null );
+    assertEquals( 2, qpm.subMap.size() );
+
+    // "a b"
+    assertNotNull( qpm.subMap.get( "a" ) );
+    qpm2 = qpm.subMap.get( "a" );
+    assertFalse( qpm2.terminal );
+    assertEquals( 1, qpm2.subMap.size() );
+    assertNotNull( qpm2.subMap.get( "b" ) );
+    qpm3 = qpm2.subMap.get( "b" );
+    assertTrue( qpm3.terminal );
+    assertEquals( 1F, qpm3.boost );
+
+    // "c d"^2
+    assertNotNull( qpm.subMap.get( "c" ) );
+    qpm2 = qpm.subMap.get( "c" );
+    assertFalse( qpm2.terminal );
+    assertEquals( 1, qpm2.subMap.size() );
+    assertNotNull( qpm2.subMap.get( "d" ) );
+    qpm3 = qpm2.subMap.get( "d" );
+    assertTrue( qpm3.terminal );
+    assertEquals( 2F, qpm3.boost );
+  }
+  
+  /*
+   * <t>...terminal
+   * 
+   * a-b-c-<t>
+   *     +-d-<t>
+   * b-c-d-<t>
+   * +-d-<t>
+   */
+  public void testQueryPhraseMapOverlapPhrases() throws Exception {
+    BooleanQuery query = new BooleanQuery();
+    query.add( pqF( "a", "b", "c" ), Occur.SHOULD );
+    query.add( pqF( 2, "b", "c", "d" ), Occur.SHOULD );
+    query.add( pqF( 3, "b", "d" ), Occur.SHOULD );
+    
+    // phraseHighlight = true, fieldMatch = true
+    FieldQuery fq = new FieldQuery( query, true, true );
+    Map<String, QueryPhraseMap> map = fq.rootMaps;
+    assertEquals( 1, map.size() );
+    assertNull( map.get( null ) );
+    assertNotNull( map.get( F ) );
+    QueryPhraseMap qpm = map.get( F );
+    assertEquals( 2, qpm.subMap.size() );
+
+    // "a b c"
+    assertNotNull( qpm.subMap.get( "a" ) );
+    QueryPhraseMap qpm2 = qpm.subMap.get( "a" );
+    assertFalse( qpm2.terminal );
+    assertEquals( 1, qpm2.subMap.size() );
+    assertNotNull( qpm2.subMap.get( "b" ) );
+    QueryPhraseMap qpm3 = qpm2.subMap.get( "b" );
+    assertFalse( qpm3.terminal );
+    assertEquals( 1, qpm3.subMap.size() );
+    assertNotNull( qpm3.subMap.get( "c" ) );
+    QueryPhraseMap qpm4 = qpm3.subMap.get( "c" );
+    assertTrue( qpm4.terminal );
+    assertEquals( 1F, qpm4.boost );
+    assertNotNull( qpm4.subMap.get( "d" ) );
+    QueryPhraseMap qpm5 = qpm4.subMap.get( "d" );
+    assertTrue( qpm5.terminal );
+    assertEquals( 1F, qpm5.boost );
+
+    // "b c d"^2, "b d"^3
+    assertNotNull( qpm.subMap.get( "b" ) );
+    qpm2 = qpm.subMap.get( "b" );
+    assertFalse( qpm2.terminal );
+    assertEquals( 2, qpm2.subMap.size() );
+    assertNotNull( qpm2.subMap.get( "c" ) );
+    qpm3 = qpm2.subMap.get( "c" );
+    assertFalse( qpm3.terminal );
+    assertEquals( 1, qpm3.subMap.size() );
+    assertNotNull( qpm3.subMap.get( "d" ) );
+    qpm4 = qpm3.subMap.get( "d" );
+    assertTrue( qpm4.terminal );
+    assertEquals( 2F, qpm4.boost );
+    assertNotNull( qpm2.subMap.get( "d" ) );
+    qpm3 = qpm2.subMap.get( "d" );
+    assertTrue( qpm3.terminal );
+    assertEquals( 3F, qpm3.boost );
+  }
+  
+  /*
+   * <t>...terminal
+   * 
+   * a-b-<t>
+   *   +-c-<t>
+   */
+  public void testQueryPhraseMapOverlapPhrases2() throws Exception {
+    BooleanQuery query = new BooleanQuery();
+    query.add( pqF( "a", "b" ), Occur.SHOULD );
+    query.add( pqF( 2, "a", "b", "c" ), Occur.SHOULD );
+    
+    // phraseHighlight = true, fieldMatch = true
+    FieldQuery fq = new FieldQuery( query, true, true );
+    Map<String, QueryPhraseMap> map = fq.rootMaps;
+    assertEquals( 1, map.size() );
+    assertNull( map.get( null ) );
+    assertNotNull( map.get( F ) );
+    QueryPhraseMap qpm = map.get( F );
+    assertEquals( 1, qpm.subMap.size() );
+
+    // "a b"
+    assertNotNull( qpm.subMap.get( "a" ) );
+    QueryPhraseMap qpm2 = qpm.subMap.get( "a" );
+    assertFalse( qpm2.terminal );
+    assertEquals( 1, qpm2.subMap.size() );
+    assertNotNull( qpm2.subMap.get( "b" ) );
+    QueryPhraseMap qpm3 = qpm2.subMap.get( "b" );
+    assertTrue( qpm3.terminal );
+    assertEquals( 1F, qpm3.boost );
+
+    // "a b c"^2
+    assertEquals( 1, qpm3.subMap.size() );
+    assertNotNull( qpm3.subMap.get( "c" ) );
+    QueryPhraseMap qpm4 = qpm3.subMap.get( "c" );
+    assertTrue( qpm4.terminal );
+    assertEquals( 2F, qpm4.boost );
+  }
+  
+  /*
+   * <t>...terminal
+   * 
+   * a-a-a-<t>
+   *     +-a-<t>
+   *       +-a-<t>
+   *         +-a-<t>
+   */
+  public void testQueryPhraseMapOverlapPhrases3() throws Exception {
+    BooleanQuery query = new BooleanQuery();
+    query.add( pqF( "a", "a", "a", "a" ), Occur.SHOULD );
+    query.add( pqF( 2, "a", "a", "a" ), Occur.SHOULD );
+    
+    // phraseHighlight = true, fieldMatch = true
+    FieldQuery fq = new FieldQuery( query, true, true );
+    Map<String, QueryPhraseMap> map = fq.rootMaps;
+    assertEquals( 1, map.size() );
+    assertNull( map.get( null ) );
+    assertNotNull( map.get( F ) );
+    QueryPhraseMap qpm = map.get( F );
+    assertEquals( 1, qpm.subMap.size() );
+
+    // "a a a"
+    assertNotNull( qpm.subMap.get( "a" ) );
+    QueryPhraseMap qpm2 = qpm.subMap.get( "a" );
+    assertFalse( qpm2.terminal );
+    assertEquals( 1, qpm2.subMap.size() );
+    assertNotNull( qpm2.subMap.get( "a" ) );
+    QueryPhraseMap qpm3 = qpm2.subMap.get( "a" );
+    assertFalse( qpm3.terminal );
+    assertEquals( 1, qpm3.subMap.size() );
+    assertNotNull( qpm3.subMap.get( "a" ) );
+    QueryPhraseMap qpm4 = qpm3.subMap.get( "a" );
+    assertTrue( qpm4.terminal );
+
+    // "a a a a"
+    assertEquals( 1, qpm4.subMap.size() );
+    assertNotNull( qpm4.subMap.get( "a" ) );
+    QueryPhraseMap qpm5 = qpm4.subMap.get( "a" );
+    assertTrue( qpm5.terminal );
+
+    // "a a a a a"
+    assertEquals( 1, qpm5.subMap.size() );
+    assertNotNull( qpm5.subMap.get( "a" ) );
+    QueryPhraseMap qpm6 = qpm5.subMap.get( "a" );
+    assertTrue( qpm6.terminal );
+
+    // "a a a a a a"
+    assertEquals( 1, qpm6.subMap.size() );
+    assertNotNull( qpm6.subMap.get( "a" ) );
+    QueryPhraseMap qpm7 = qpm6.subMap.get( "a" );
+    assertTrue( qpm7.terminal );
+  }
+  
+  public void testQueryPhraseMapOverlap2gram() throws Exception {
+    Query query = paB.parse( "abc AND bcd" );
+    
+    // phraseHighlight = true, fieldMatch = true
+    FieldQuery fq = new FieldQuery( query, true, true );
+    Map<String, QueryPhraseMap> map = fq.rootMaps;
+    assertEquals( 1, map.size() );
+    assertNull( map.get( null ) );
+    assertNotNull( map.get( F ) );
+    QueryPhraseMap qpm = map.get( F );
+    assertEquals( 2, qpm.subMap.size() );
+
+    // "ab bc"
+    assertNotNull( qpm.subMap.get( "ab" ) );
+    QueryPhraseMap qpm2 = qpm.subMap.get( "ab" );
+    assertFalse( qpm2.terminal );
+    assertEquals( 1, qpm2.subMap.size() );
+    assertNotNull( qpm2.subMap.get( "bc" ) );
+    QueryPhraseMap qpm3 = qpm2.subMap.get( "bc" );
+    assertTrue( qpm3.terminal );
+    assertEquals( 1F, qpm3.boost );
+
+    // "ab bc cd"
+    assertEquals( 1, qpm3.subMap.size() );
+    assertNotNull( qpm3.subMap.get( "cd" ) );
+    QueryPhraseMap qpm4 = qpm3.subMap.get( "cd" );
+    assertTrue( qpm4.terminal );
+    assertEquals( 1F, qpm4.boost );
+
+    // "bc cd"
+    assertNotNull( qpm.subMap.get( "bc" ) );
+    qpm2 = qpm.subMap.get( "bc" );
+    assertFalse( qpm2.terminal );
+    assertEquals( 1, qpm2.subMap.size() );
+    assertNotNull( qpm2.subMap.get( "cd" ) );
+    qpm3 = qpm2.subMap.get( "cd" );
+    assertTrue( qpm3.terminal );
+    assertEquals( 1F, qpm3.boost );
+    
+    // phraseHighlight = false, fieldMatch = true
+    fq = new FieldQuery( query, false, true );
+    map = fq.rootMaps;
+    assertEquals( 1, map.size() );
+    assertNull( map.get( null ) );
+    assertNotNull( map.get( F ) );
+    qpm = map.get( F );
+    assertEquals( 3, qpm.subMap.size() );
+
+    // "ab bc"
+    assertNotNull( qpm.subMap.get( "ab" ) );
+    qpm2 = qpm.subMap.get( "ab" );
+    assertTrue( qpm2.terminal );
+    assertEquals( 1F, qpm2.boost );
+    assertEquals( 1, qpm2.subMap.size() );
+    assertNotNull( qpm2.subMap.get( "bc" ) );
+    qpm3 = qpm2.subMap.get( "bc" );
+    assertTrue( qpm3.terminal );
+    assertEquals( 1F, qpm3.boost );
+
+    // "ab bc cd"
+    assertEquals( 1, qpm3.subMap.size() );
+    assertNotNull( qpm3.subMap.get( "cd" ) );
+    qpm4 = qpm3.subMap.get( "cd" );
+    assertTrue( qpm4.terminal );
+    assertEquals( 1F, qpm4.boost );
+
+    // "bc cd"
+    assertNotNull( qpm.subMap.get( "bc" ) );
+    qpm2 = qpm.subMap.get( "bc" );
+    assertTrue( qpm2.terminal );
+    assertEquals( 1F, qpm2.boost );
+    assertEquals( 1, qpm2.subMap.size() );
+    assertNotNull( qpm2.subMap.get( "cd" ) );
+    qpm3 = qpm2.subMap.get( "cd" );
+    assertTrue( qpm3.terminal );
+    assertEquals( 1F, qpm3.boost );
+
+    // "cd"
+    assertNotNull( qpm.subMap.get( "cd" ) );
+    qpm2 = qpm.subMap.get( "cd" );
+    assertTrue( qpm2.terminal );
+    assertEquals( 1F, qpm2.boost );
+    assertEquals( 0, qpm2.subMap.size() );
+  }
+  
+  public void testSearchPhrase() throws Exception {
+    Query query = pqF( "a", "b", "c" );
+
+    // phraseHighlight = true, fieldMatch = true
+    FieldQuery fq = new FieldQuery( query, true, true );
+    
+    // "a"
+    List<TermInfo> phraseCandidate = new ArrayList<TermInfo>();
+    phraseCandidate.add( new TermInfo( "a", 0, 1, 0 ) );
+    assertNull( fq.searchPhrase( F, phraseCandidate ) );
+    // "a b"
+    phraseCandidate.add( new TermInfo( "b", 2, 3, 1 ) );
+    assertNull( fq.searchPhrase( F, phraseCandidate ) );
+    // "a b c"
+    phraseCandidate.add( new TermInfo( "c", 4, 5, 2 ) );
+    assertNotNull( fq.searchPhrase( F, phraseCandidate ) );
+    assertNull( fq.searchPhrase( "x", phraseCandidate ) );
+
+    // phraseHighlight = true, fieldMatch = false
+    fq = new FieldQuery( query, true, false );
+    
+    // "a b c"
+    assertNotNull( fq.searchPhrase( F, phraseCandidate ) );
+    assertNotNull( fq.searchPhrase( "x", phraseCandidate ) );
+
+    // phraseHighlight = false, fieldMatch = true
+    fq = new FieldQuery( query, false, true );
+    
+    // "a"
+    phraseCandidate.clear();
+    phraseCandidate.add( new TermInfo( "a", 0, 1, 0 ) );
+    assertNotNull( fq.searchPhrase( F, phraseCandidate ) );
+    // "a b"
+    phraseCandidate.add( new TermInfo( "b", 2, 3, 1 ) );
+    assertNull( fq.searchPhrase( F, phraseCandidate ) );
+    // "a b c"
+    phraseCandidate.add( new TermInfo( "c", 4, 5, 2 ) );
+    assertNotNull( fq.searchPhrase( F, phraseCandidate ) );
+    assertNull( fq.searchPhrase( "x", phraseCandidate ) );
+  }
+  
+  public void testSearchPhraseSlop() throws Exception {
+    // "a b c"~0
+    Query query = pqF( "a", "b", "c" );
+
+    // phraseHighlight = true, fieldMatch = true
+    FieldQuery fq = new FieldQuery( query, true, true );
+    
+    // "a b c" w/ position-gap = 2
+    List<TermInfo> phraseCandidate = new ArrayList<TermInfo>();
+    phraseCandidate.add( new TermInfo( "a", 0, 1, 0 ) );
+    phraseCandidate.add( new TermInfo( "b", 2, 3, 2 ) );
+    phraseCandidate.add( new TermInfo( "c", 4, 5, 4 ) );
+    assertNull( fq.searchPhrase( F, phraseCandidate ) );
+
+    // "a b c"~1
+    query = pqF( 1F, 1, "a", "b", "c" );
+
+    // phraseHighlight = true, fieldMatch = true
+    fq = new FieldQuery( query, true, true );
+    
+    // "a b c" w/ position-gap = 2
+    assertNotNull( fq.searchPhrase( F, phraseCandidate ) );
+    
+    // "a b c" w/ position-gap = 3
+    phraseCandidate.clear();
+    phraseCandidate.add( new TermInfo( "a", 0, 1, 0 ) );
+    phraseCandidate.add( new TermInfo( "b", 2, 3, 3 ) );
+    phraseCandidate.add( new TermInfo( "c", 4, 5, 6 ) );
+    assertNull( fq.searchPhrase( F, phraseCandidate ) );
+  }
+}

Property changes on: contrib/fast-vector-highlighter/src/test/org/apache/lucene/search/vectorhighlight/FieldQueryTest.java
___________________________________________________________________
Name: svn:keywords
   + Date Author Id Revision HeadURL
Name: svn:eol-style
   + native

Index: contrib/fast-vector-highlighter/src/test/org/apache/lucene/search/vectorhighlight/SimpleFragListBuilderTest.java
===================================================================
--- contrib/fast-vector-highlighter/src/test/org/apache/lucene/search/vectorhighlight/SimpleFragListBuilderTest.java	(revision 0)
+++ contrib/fast-vector-highlighter/src/test/org/apache/lucene/search/vectorhighlight/SimpleFragListBuilderTest.java	(revision 0)
@@ -0,0 +1,162 @@
+package org.apache.lucene.search.vectorhighlight;
+
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import org.apache.lucene.search.Query;
+
+public class SimpleFragListBuilderTest extends AbstractTestCase {
+  
+  public void testNullFieldFragList() throws Exception {
+    SimpleFragListBuilder sflb = new SimpleFragListBuilder();
+    FieldFragList ffl = sflb.createFieldFragList( fpl( "a", "b c d" ), 100 );
+    assertEquals( 0, ffl.fragInfos.size() );
+  }
+  
+  public void testTooSmallFragSize() throws Exception {
+    try{
+      SimpleFragListBuilder sflb = new SimpleFragListBuilder();
+      sflb.createFieldFragList( fpl( "a", "b c d" ), SimpleFragListBuilder.MIN_FRAG_CHAR_SIZE - 1 );
+      fail( "IllegalArgumentException must be thrown" );
+    }
+    catch ( IllegalArgumentException expected ) {
+    }
+  }
+  
+  public void test1TermIndex() throws Exception {
+    SimpleFragListBuilder sflb = new SimpleFragListBuilder();
+    FieldFragList ffl = sflb.createFieldFragList( fpl( "a", "a" ), 100 );
+    assertEquals( 1, ffl.fragInfos.size() );
+    assertEquals( "subInfos=(a((0,1)))/1.0(0,100)", ffl.fragInfos.get( 0 ).toString() );
+  }
+  
+  public void test2TermsIndex1Frag() throws Exception {
+    SimpleFragListBuilder sflb = new SimpleFragListBuilder();
+    FieldFragList ffl = sflb.createFieldFragList( fpl( "a", "a a" ), 100 );
+    assertEquals( 1, ffl.fragInfos.size() );
+    assertEquals( "subInfos=(a((0,1))a((2,3)))/2.0(0,100)", ffl.fragInfos.get( 0 ).toString() );
+
+    ffl = sflb.createFieldFragList( fpl( "a", "a b b b b b b b b a" ), 20 );
+    assertEquals( 1, ffl.fragInfos.size() );
+    assertEquals( "subInfos=(a((0,1))a((18,19)))/2.0(0,20)", ffl.fragInfos.get( 0 ).toString() );
+    
+    ffl = sflb.createFieldFragList( fpl( "a", "b b b b a b b b b a" ), 20 );
+    assertEquals( 1, ffl.fragInfos.size() );
+    assertEquals( "subInfos=(a((8,9))a((18,19)))/2.0(2,22)", ffl.fragInfos.get( 0 ).toString() );
+  }
+  
+  public void test2TermsIndex2Frags() throws Exception {
+    SimpleFragListBuilder sflb = new SimpleFragListBuilder();
+    FieldFragList ffl = sflb.createFieldFragList( fpl( "a", "a b b b b b b b b b b b b b a" ), 20 );
+    assertEquals( 2, ffl.fragInfos.size() );
+    assertEquals( "subInfos=(a((0,1)))/1.0(0,20)", ffl.fragInfos.get( 0 ).toString() );
+    assertEquals( "subInfos=(a((28,29)))/1.0(22,42)", ffl.fragInfos.get( 1 ).toString() );
+
+    ffl = sflb.createFieldFragList( fpl( "a", "a b b b b b b b b b b b b a" ), 20 );
+    assertEquals( 2, ffl.fragInfos.size() );
+    assertEquals( "subInfos=(a((0,1)))/1.0(0,20)", ffl.fragInfos.get( 0 ).toString() );
+    assertEquals( "subInfos=(a((26,27)))/1.0(20,40)", ffl.fragInfos.get( 1 ).toString() );
+
+    ffl = sflb.createFieldFragList( fpl( "a", "a b b b b b b b b b a" ), 20 );
+    assertEquals( 2, ffl.fragInfos.size() );
+    assertEquals( "subInfos=(a((0,1)))/1.0(0,20)", ffl.fragInfos.get( 0 ).toString() );
+    assertEquals( "subInfos=(a((20,21)))/1.0(20,40)", ffl.fragInfos.get( 1 ).toString() );
+  }
+  
+  public void test2TermsQuery() throws Exception {
+    SimpleFragListBuilder sflb = new SimpleFragListBuilder();
+    FieldFragList ffl = sflb.createFieldFragList( fpl( "a b", "c d e" ), 20 );
+    assertEquals( 0, ffl.fragInfos.size() );
+
+    ffl = sflb.createFieldFragList( fpl( "a b", "d b c" ), 20 );
+    assertEquals( 1, ffl.fragInfos.size() );
+    assertEquals( "subInfos=(b((2,3)))/1.0(0,20)", ffl.fragInfos.get( 0 ).toString() );
+
+    ffl = sflb.createFieldFragList( fpl( "a b", "a b c" ), 20 );
+    assertEquals( 1, ffl.fragInfos.size() );
+    assertEquals( "subInfos=(a((0,1))b((2,3)))/2.0(0,20)", ffl.fragInfos.get( 0 ).toString() );
+  }
+  
+  public void testPhraseQuery() throws Exception {
+    SimpleFragListBuilder sflb = new SimpleFragListBuilder();
+    FieldFragList ffl = sflb.createFieldFragList( fpl( "\"a b\"", "c d e" ), 20 );
+    assertEquals( 0, ffl.fragInfos.size() );
+
+    ffl = sflb.createFieldFragList( fpl( "\"a b\"", "a c b" ), 20 );
+    assertEquals( 0, ffl.fragInfos.size() );
+
+    ffl = sflb.createFieldFragList( fpl( "\"a b\"", "a b c" ), 20 );
+    assertEquals( 1, ffl.fragInfos.size() );
+    assertEquals( "subInfos=(ab((0,3)))/1.0(0,20)", ffl.fragInfos.get( 0 ).toString() );
+  }
+  
+  public void testPhraseQuerySlop() throws Exception {
+    SimpleFragListBuilder sflb = new SimpleFragListBuilder();
+    FieldFragList ffl = sflb.createFieldFragList( fpl( "\"a b\"~1", "a c b" ), 20 );
+    assertEquals( 1, ffl.fragInfos.size() );
+    assertEquals( "subInfos=(ab((0,1)(4,5)))/1.0(0,20)", ffl.fragInfos.get( 0 ).toString() );
+  }
+
+  private FieldPhraseList fpl( String queryValue, String indexValue ) throws Exception {
+    make1d1fIndex( indexValue );
+    Query query = paW.parse( queryValue );
+    FieldQuery fq = new FieldQuery( query, true, true );
+    FieldTermStack stack = new FieldTermStack( reader, 0, F, fq );
+    return new FieldPhraseList( stack, fq );
+  }
+  
+  public void test1PhraseShortMV() throws Exception {
+    makeIndexShortMV();
+
+    FieldQuery fq = new FieldQuery( tq( "d" ), true, true );
+    FieldTermStack stack = new FieldTermStack( reader, 0, F, fq );
+    FieldPhraseList fpl = new FieldPhraseList( stack, fq );
+    SimpleFragListBuilder sflb = new SimpleFragListBuilder();
+    FieldFragList ffl = sflb.createFieldFragList( fpl, 100 );
+    assertEquals( 1, ffl.fragInfos.size() );
+    assertEquals( "subInfos=(d((6,7)))/1.0(0,100)", ffl.fragInfos.get( 0 ).toString() );
+  }
+  
+  public void test1PhraseLongMV() throws Exception {
+    makeIndexLongMV();
+
+    FieldQuery fq = new FieldQuery( pqF( "search", "engines" ), true, true );
+    FieldTermStack stack = new FieldTermStack( reader, 0, F, fq );
+    FieldPhraseList fpl = new FieldPhraseList( stack, fq );
+    SimpleFragListBuilder sflb = new SimpleFragListBuilder();
+    FieldFragList ffl = sflb.createFieldFragList( fpl, 100 );
+    assertEquals( 1, ffl.fragInfos.size() );
+    assertEquals( "subInfos=(searchengines((102,116))searchengines((157,171)))/2.0(96,196)", ffl.fragInfos.get( 0 ).toString() );
+  }
+/*
+ * ----------------------------------
+ *  THIS TEST DEPENDS ON LUCENE-1448
+ *  UNCOMMENT WHEN IT IS COMMITTED.
+ * ----------------------------------
+  public void test1PhraseLongMVB() throws Exception {
+    makeIndexLongMVB();
+
+    FieldQuery fq = new FieldQuery( pqF( "sp", "pe", "ee", "ed" ), true, true ); // "speed" -(2gram)-> "sp","pe","ee","ed"
+    FieldTermStack stack = new FieldTermStack( reader, 0, F, fq );
+    FieldPhraseList fpl = new FieldPhraseList( stack, fq );
+    SimpleFragListBuilder sflb = new SimpleFragListBuilder();
+    FieldFragList ffl = sflb.createFieldFragList( fpl, 100 );
+    assertEquals( 1, ffl.fragInfos.size() );
+    assertEquals( "subInfos=(sppeeeed((88,93)))/1.0(82,182)", ffl.fragInfos.get( 0 ).toString() );
+  }
+*/
+}

Property changes on: contrib/fast-vector-highlighter/src/test/org/apache/lucene/search/vectorhighlight/SimpleFragListBuilderTest.java
___________________________________________________________________
Name: svn:keywords
   + Date Author Id Revision HeadURL
Name: svn:eol-style
   + native

Index: contrib/fast-vector-highlighter/src/java/org/apache/lucene/search/vectorhighlight/FastVectorHighlighter.java
===================================================================
--- contrib/fast-vector-highlighter/src/java/org/apache/lucene/search/vectorhighlight/FastVectorHighlighter.java	(revision 0)
+++ contrib/fast-vector-highlighter/src/java/org/apache/lucene/search/vectorhighlight/FastVectorHighlighter.java	(revision 0)
@@ -0,0 +1,137 @@
+package org.apache.lucene.search.vectorhighlight;
+
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import java.io.IOException;
+
+import org.apache.lucene.index.IndexReader;
+import org.apache.lucene.search.Query;
+
+/**
+ * Another highlighter implementation.
+ *
+ */
+public class FastVectorHighlighter {
+
+  public static final boolean DEFAULT_PHRASE_HIGHLIGHT = true;
+  public static final boolean DEFAULT_FIELD_MATCH = true;
+  private final boolean phraseHighlight;
+  private final boolean fieldMatch;
+  private final FragListBuilder fragListBuilder;
+  private final FragmentsBuilder fragmentsBuilder;
+
+  /**
+   * the default constructor.
+   */
+  public FastVectorHighlighter(){
+    this( DEFAULT_PHRASE_HIGHLIGHT, DEFAULT_FIELD_MATCH );
+  }
+
+  /**
+   * a constructor. Using SimpleFragListBuilder and ScoreOrderFragmentsBuilder.
+   * 
+   * @param phraseHighlight true or false for phrase highlighting
+   * @param fieldMatch true of false for field matching
+   */
+  public FastVectorHighlighter( boolean phraseHighlight, boolean fieldMatch ){
+    this( phraseHighlight, fieldMatch, new SimpleFragListBuilder(), new ScoreOrderFragmentsBuilder() );
+  }
+
+  /**
+   * a constructor. A FragListBuilder and a FragmentsBuilder can be specified (plugins).
+   * 
+   * @param phraseHighlight true of false for phrase highlighting
+   * @param fieldMatch true of false for field matching
+   * @param fragListBuilder an instance of FragListBuilder
+   * @param fragmentsBuilder an instance of FragmentsBuilder
+   */
+  public FastVectorHighlighter( boolean phraseHighlight, boolean fieldMatch,
+      FragListBuilder fragListBuilder, FragmentsBuilder fragmentsBuilder ){
+    this.phraseHighlight = phraseHighlight;
+    this.fieldMatch = fieldMatch;
+    this.fragListBuilder = fragListBuilder;
+    this.fragmentsBuilder = fragmentsBuilder;
+  }
+
+  /**
+   * create a FieldQuery object.
+   * 
+   * @param query a query
+   * @return the created FieldQuery object
+   */
+  public FieldQuery getFieldQuery( Query query ){
+    return new FieldQuery( query, phraseHighlight, fieldMatch );
+  }
+
+  /**
+   * return the best fragment.
+   * 
+   * @param fieldQuery FieldQuery object
+   * @param reader IndexReader of the index
+   * @param docId document id to be highlighted
+   * @param fieldName field of the document to be highlighted
+   * @param fragCharSize the length (number of chars) of a fragment
+   * @return the best fragment (snippet) string
+   * @throws IOException
+   */
+  public final String getBestFragment( final FieldQuery fieldQuery, IndexReader reader, int docId,
+      String fieldName, int fragCharSize ) throws IOException {
+    FieldFragList fieldFragList = getFieldFragList( fieldQuery, reader, docId, fieldName, fragCharSize );
+    return fragmentsBuilder.createFragment( reader, docId, fieldName, fieldFragList );
+  }
+
+  /**
+   * return the best fragments.
+   * 
+   * @param fieldQuery FieldQuery object
+   * @param reader IndexReader of the index
+   * @param docId document id to be highlighted
+   * @param fieldName field of the document to be highlighted
+   * @param fragCharSize the length (number of chars) of a fragment
+   * @param maxNumFragments maximum number of fragments
+   * @return created fragments or null when no fragments created.
+   *         size of the array can be less than maxNumFragments
+   * @throws IOException
+   */
+  public final String[] getBestFragments( final FieldQuery fieldQuery, IndexReader reader, int docId,
+      String fieldName, int fragCharSize, int maxNumFragments ) throws IOException {
+    FieldFragList fieldFragList = getFieldFragList( fieldQuery, reader, docId, fieldName, fragCharSize );
+    return fragmentsBuilder.createFragments( reader, docId, fieldName, fieldFragList, maxNumFragments );
+  }
+  
+  private FieldFragList getFieldFragList( final FieldQuery fieldQuery, IndexReader reader, int docId,
+      String fieldName, int fragCharSize ) throws IOException {
+    FieldTermStack fieldTermStack = new FieldTermStack( reader, docId, fieldName, fieldQuery );
+    FieldPhraseList fieldPhraseList = new FieldPhraseList( fieldTermStack, fieldQuery );
+    return fragListBuilder.createFieldFragList( fieldPhraseList, fragCharSize );
+  }
+
+  /**
+   * return whether phraseHighlight or not.
+   * 
+   * @return
+   */
+  public boolean isPhraseHighlight(){ return phraseHighlight; }
+
+  /**
+   * return whether fieldMatch or not.
+   * 
+   * @return
+   */
+  public boolean isFieldMatch(){ return fieldMatch; }
+}

Property changes on: contrib/fast-vector-highlighter/src/java/org/apache/lucene/search/vectorhighlight/FastVectorHighlighter.java
___________________________________________________________________
Name: svn:keywords
   + Date Author Id Revision HeadURL
Name: svn:eol-style
   + native

Index: contrib/fast-vector-highlighter/src/java/org/apache/lucene/search/vectorhighlight/FieldFragList.java
===================================================================
--- contrib/fast-vector-highlighter/src/java/org/apache/lucene/search/vectorhighlight/FieldFragList.java	(revision 0)
+++ contrib/fast-vector-highlighter/src/java/org/apache/lucene/search/vectorhighlight/FieldFragList.java	(revision 0)
@@ -0,0 +1,103 @@
+package org.apache.lucene.search.vectorhighlight;
+
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.apache.lucene.search.vectorhighlight.FieldPhraseList.WeightedPhraseInfo;
+import org.apache.lucene.search.vectorhighlight.FieldPhraseList.WeightedPhraseInfo.Toffs;
+
+/**
+ * FieldFragList has a list of "frag info" that is used by FragmentsBuilder class
+ * to create fragments (snippets).
+ */
+public class FieldFragList {
+
+  private final int fragCharSize;
+  List<WeightedFragInfo> fragInfos = new ArrayList<WeightedFragInfo>();
+
+  /**
+   * a constructor.
+   * 
+   * @param fragCharSize the length (number of chars) of a fragment
+   */
+  public FieldFragList( int fragCharSize ){
+    this.fragCharSize = fragCharSize;
+  }
+
+  /**
+   * convert the list of WeightedPhraseInfo to WeightedFragInfo, then add it to the fragInfos
+   * 
+   * @param startOffset start offset of the fragment
+   * @param endOffset end offset of the fragment
+   * @param phraseInfoList list of WeightedPhraseInfo objects
+   */
+  public void add( int startOffset, int endOffset, List<WeightedPhraseInfo> phraseInfoList ){
+    fragInfos.add( new WeightedFragInfo( startOffset, endOffset, phraseInfoList ) );
+  }
+  
+  public static class WeightedFragInfo {
+
+    List<SubInfo> subInfos;
+    float totalBoost;
+    int startOffset;
+    int endOffset;
+
+    public WeightedFragInfo( int startOffset, int endOffset, List<WeightedPhraseInfo> phraseInfoList ){
+      this.startOffset = startOffset;
+      this.endOffset = endOffset;
+      subInfos = new ArrayList<SubInfo>();
+      for( WeightedPhraseInfo phraseInfo : phraseInfoList ){
+        SubInfo subInfo = new SubInfo( phraseInfo.text, phraseInfo.termsOffsets, phraseInfo.seqnum );
+        subInfos.add( subInfo );
+        totalBoost += phraseInfo.boost;
+      }
+    }
+    
+    public String toString(){
+      StringBuilder sb = new StringBuilder();
+      sb.append( "subInfos=(" );
+      for( SubInfo si : subInfos )
+        sb.append( si.toString() );
+      sb.append( ")/" ).append( totalBoost ).append( '(' ).append( startOffset ).append( ',' ).append( endOffset ).append( ')' );
+      return sb.toString();
+    }
+    
+    static class SubInfo {
+      final String text;  // unnecessary member, just exists for debugging purpose
+      final List<Toffs> termsOffsets;   // usually termsOffsets.size() == 1,
+                              // but if position-gap > 1 and slop > 0 then size() could be greater than 1
+      int seqnum;
+      SubInfo( String text, List<Toffs> termsOffsets, int seqnum ){
+        this.text = text;
+        this.termsOffsets = termsOffsets;
+        this.seqnum = seqnum;
+      }
+      
+      public String toString(){
+        StringBuilder sb = new StringBuilder();
+        sb.append( text ).append( '(' );
+        for( Toffs to : termsOffsets )
+          sb.append( to.toString() );
+        sb.append( ')' );
+        return sb.toString();
+      }
+    }
+  }
+}

Property changes on: contrib/fast-vector-highlighter/src/java/org/apache/lucene/search/vectorhighlight/FieldFragList.java
___________________________________________________________________
Name: svn:keywords
   + Date Author Id Revision HeadURL
Name: svn:eol-style
   + native

Index: contrib/fast-vector-highlighter/src/java/org/apache/lucene/search/vectorhighlight/FieldTermStack.java
===================================================================
--- contrib/fast-vector-highlighter/src/java/org/apache/lucene/search/vectorhighlight/FieldTermStack.java	(revision 0)
+++ contrib/fast-vector-highlighter/src/java/org/apache/lucene/search/vectorhighlight/FieldTermStack.java	(revision 0)
@@ -0,0 +1,171 @@
+package org.apache.lucene.search.vectorhighlight;
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import java.io.IOException;
+import java.util.Collections;
+import java.util.LinkedList;
+import java.util.Set;
+
+import org.apache.lucene.analysis.Analyzer;
+import org.apache.lucene.analysis.WhitespaceAnalyzer;
+import org.apache.lucene.document.Document;
+import org.apache.lucene.document.Field;
+import org.apache.lucene.document.Field.Index;
+import org.apache.lucene.document.Field.Store;
+import org.apache.lucene.document.Field.TermVector;
+import org.apache.lucene.index.IndexReader;
+import org.apache.lucene.index.IndexWriter;
+import org.apache.lucene.index.TermFreqVector;
+import org.apache.lucene.index.TermPositionVector;
+import org.apache.lucene.index.TermVectorOffsetInfo;
+import org.apache.lucene.index.IndexWriter.MaxFieldLength;
+import org.apache.lucene.queryParser.QueryParser;
+import org.apache.lucene.search.Query;
+import org.apache.lucene.store.Directory;
+import org.apache.lucene.store.RAMDirectory;
+
+/**
+ * <code>FieldTermStack</code> is a stack that keeps query terms in the specified field
+ * of the document to be highlighted.
+ */
+public class FieldTermStack {
+  
+  private final String fieldName;
+  LinkedList<TermInfo> termList = new LinkedList<TermInfo>();
+  
+  public static void main( String[] args ) throws Exception {
+    Analyzer analyzer = new WhitespaceAnalyzer();
+    QueryParser parser = new QueryParser( "f", analyzer );
+    Query query = parser.parse( "a x:b" );
+    FieldQuery fieldQuery = new FieldQuery( query, true, false );
+    
+    Directory dir = new RAMDirectory();
+    IndexWriter writer = new IndexWriter( dir, analyzer, MaxFieldLength.LIMITED );
+    Document doc = new Document();
+    doc.add( new Field( "f", "a a a b b c a b b c d e f", Store.YES, Index.ANALYZED, TermVector.WITH_POSITIONS_OFFSETS ) );
+    doc.add( new Field( "f", "b a b a f", Store.YES, Index.ANALYZED, TermVector.WITH_POSITIONS_OFFSETS ) );
+    writer.addDocument( doc );
+    writer.close();
+    
+    IndexReader reader = IndexReader.open( dir );
+    FieldTermStack ftl = new FieldTermStack( reader, 0, "f", fieldQuery );
+    reader.close();
+  }
+
+  /**
+   * a constructor.
+   * 
+   * @param reader IndexReader of the index
+   * @param docId document id to be highlighted
+   * @param fieldName field of the document to be highlighted
+   * @param fieldQuery FieldQuery object
+   * @throws IOException
+   */
+  public FieldTermStack( IndexReader reader, int docId, String fieldName, final FieldQuery fieldQuery ) throws IOException {
+    this.fieldName = fieldName;
+
+    TermFreqVector tfv = reader.getTermFreqVector( docId, fieldName );
+    if( tfv == null ) return; // just return to make null snippets
+    TermPositionVector tpv = null;
+    try{
+      tpv = (TermPositionVector)tfv;
+    }
+    catch( ClassCastException e ){
+      return; // just return to make null snippets
+    }
+    
+    Set<String> termSet = fieldQuery.getTermSet( fieldName );
+    // just return to make null snippet if un-matched fieldName specified when fieldMatch == true
+    if( termSet == null ) return;
+    
+    for( String term : tpv.getTerms() ){
+      if( !termSet.contains( term ) ) continue;
+      int index = tpv.indexOf( term );
+      TermVectorOffsetInfo[] tvois = tpv.getOffsets( index );
+      if( tvois == null ) return; // just return to make null snippets
+      int[] poss = tpv.getTermPositions( index );
+      if( poss == null ) return; // just return to make null snippets
+      for( int i = 0; i < tvois.length; i++ )
+        termList.add( new TermInfo( term, tvois[i].getStartOffset(), tvois[i].getEndOffset(), poss[i] ) );
+    }
+    
+    // sort by position
+    Collections.sort( termList );
+  }
+
+  /**
+   * @return field name
+   */
+  public String getFieldName(){
+    return fieldName;
+  }
+
+  /**
+   * @return the top TermInfo object of the stack
+   */
+  public TermInfo pop(){
+    return termList.poll();
+  }
+
+  /**
+   * @param termInfo the TermInfo object to be put on the top of the stack
+   */
+  public void push( TermInfo termInfo ){
+    // termList.push( termInfo );  // avoid Java 1.6 feature
+    termList.addFirst( termInfo );
+  }
+
+  /**
+   * to know whether the stack is empty
+   * 
+   * @return true if the stack is empty, false if not
+   */
+  public boolean isEmpty(){
+    return termList == null || termList.size() == 0;
+  }
+  
+  public static class TermInfo implements Comparable<TermInfo>{
+
+    final String text;
+    final int startOffset;
+    final int endOffset;
+    final int position;
+
+    TermInfo( String text, int startOffset, int endOffset, int position ){
+      this.text = text;
+      this.startOffset = startOffset;
+      this.endOffset = endOffset;
+      this.position = position;
+    }
+    
+    public String getText(){ return text; }
+    public int getStartOffset(){ return startOffset; }
+    public int getEndOffset(){ return endOffset; }
+    public int getPosition(){ return position; }
+    
+    public String toString(){
+      StringBuilder sb = new StringBuilder();
+      sb.append( text ).append( '(' ).append(startOffset).append( ',' ).append( endOffset ).append( ',' ).append( position ).append( ')' );
+      return sb.toString();
+    }
+
+    public int compareTo( TermInfo o ) {
+      return ( this.position - o.position );
+    }
+  }
+}

Property changes on: contrib/fast-vector-highlighter/src/java/org/apache/lucene/search/vectorhighlight/FieldTermStack.java
___________________________________________________________________
Name: svn:keywords
   + Date Author Id Revision HeadURL
Name: svn:eol-style
   + native

Index: contrib/fast-vector-highlighter/src/java/org/apache/lucene/search/vectorhighlight/FieldPhraseList.java
===================================================================
--- contrib/fast-vector-highlighter/src/java/org/apache/lucene/search/vectorhighlight/FieldPhraseList.java	(revision 0)
+++ contrib/fast-vector-highlighter/src/java/org/apache/lucene/search/vectorhighlight/FieldPhraseList.java	(revision 0)
@@ -0,0 +1,183 @@
+package org.apache.lucene.search.vectorhighlight;
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import java.util.ArrayList;
+import java.util.LinkedList;
+import java.util.List;
+
+import org.apache.lucene.search.vectorhighlight.FieldQuery.QueryPhraseMap;
+import org.apache.lucene.search.vectorhighlight.FieldTermStack.TermInfo;
+
+/**
+ * FieldPhraseList has a list of WeightedPhraseInfo that is used by FragListBuilder
+ * to create a FieldFragList object.
+ */
+public class FieldPhraseList {
+
+  LinkedList<WeightedPhraseInfo> phraseList = new LinkedList<WeightedPhraseInfo>();
+
+  /**
+   * a constructor.
+   * 
+   * @param fieldTermStack FieldTermStack object
+   * @param fieldQuery FieldQuery object
+   */
+  public FieldPhraseList( FieldTermStack fieldTermStack, FieldQuery fieldQuery ){
+    final String field = fieldTermStack.getFieldName();
+
+    LinkedList<TermInfo> phraseCandidate = new LinkedList<TermInfo>();
+    QueryPhraseMap currMap = null;
+    QueryPhraseMap nextMap = null;
+    while( !fieldTermStack.isEmpty() ){
+      
+      phraseCandidate.clear();
+
+      TermInfo ti = fieldTermStack.pop();
+      currMap = fieldQuery.getFieldTermMap( field, ti.getText() );
+
+      // if not found, discard top TermInfo from stack, then try next element
+      if( currMap == null ) continue;
+      
+      // if found, search the longest phrase
+      phraseCandidate.add( ti );
+      while( true ){
+        ti = fieldTermStack.pop();
+        nextMap = null;
+        if( ti != null )
+          nextMap = currMap.getTermMap( ti.getText() );
+        if( ti == null || nextMap == null ){
+          if( ti != null )
+            fieldTermStack.push( ti );
+          if( currMap.isValidTermOrPhrase( phraseCandidate ) ){
+            addIfNoOverlap( new WeightedPhraseInfo( phraseCandidate, currMap.getBoost(), currMap.getTermOrPhraseNumber() ) );
+          }
+          else{
+            while( phraseCandidate.size() > 1 ){
+              fieldTermStack.push( phraseCandidate.removeLast() );
+              currMap = fieldQuery.searchPhrase( field, phraseCandidate );
+              if( currMap != null ){
+                addIfNoOverlap( new WeightedPhraseInfo( phraseCandidate, currMap.getBoost(), currMap.getTermOrPhraseNumber() ) );
+                break;
+              }
+            }
+          }
+          break;
+        }
+        else{
+          phraseCandidate.add( ti );
+          currMap = nextMap;
+        }
+      }
+    }
+  }
+  
+  void addIfNoOverlap( WeightedPhraseInfo wpi ){
+    for( WeightedPhraseInfo existWpi : phraseList ){
+      if( existWpi.isOffsetOverlap( wpi ) ) return;
+    }
+    phraseList.add( wpi );
+  }
+  
+  public static class WeightedPhraseInfo {
+
+    String text;  // unnecessary member, just exists for debugging purpose
+    List<Toffs> termsOffsets;   // usually termsOffsets.size() == 1,
+                            // but if position-gap > 1 and slop > 0 then size() could be greater than 1
+    float boost;  // query boost
+    int seqnum;
+    
+    public WeightedPhraseInfo( LinkedList<TermInfo> terms, float boost ){
+      this( terms, boost, 0 );
+    }
+    
+    public WeightedPhraseInfo( LinkedList<TermInfo> terms, float boost, int number ){
+      this.boost = boost;
+      this.seqnum = number;
+      termsOffsets = new ArrayList<Toffs>( terms.size() );
+      TermInfo ti = terms.get( 0 );
+      termsOffsets.add( new Toffs( ti.getStartOffset(), ti.getEndOffset() ) );
+      if( terms.size() == 1 ){
+        text = ti.getText();
+        return;
+      }
+      StringBuilder sb = new StringBuilder();
+      sb.append( ti.getText() );
+      int pos = ti.getPosition();
+      for( int i = 1; i < terms.size(); i++ ){
+        ti = terms.get( i );
+        sb.append( ti.getText() );
+        if( ti.getPosition() - pos == 1 ){
+          Toffs to = termsOffsets.get( termsOffsets.size() - 1 );
+          to.setEndOffset( ti.getEndOffset() );
+        }
+        else{
+          termsOffsets.add( new Toffs( ti.getStartOffset(), ti.getEndOffset() ) );
+        }
+        pos = ti.getPosition();
+      }
+      text = sb.toString();
+    }
+    
+    public int getStartOffset(){
+      return termsOffsets.get( 0 ).startOffset;
+    }
+    
+    public int getEndOffset(){
+      return termsOffsets.get( termsOffsets.size() - 1 ).endOffset;
+    }
+    
+    public boolean isOffsetOverlap( WeightedPhraseInfo other ){
+      int so = getStartOffset();
+      int eo = getEndOffset();
+      int oso = other.getStartOffset();
+      int oeo = other.getEndOffset();
+      if( so <= oso && oso <= eo ) return true;
+      if( so <= oeo && oeo <= eo ) return true;
+      if( oso <= so && so <= oeo ) return true;
+      if( oso <= eo && eo <= oeo ) return true;
+      return false;
+    }
+    
+    public String toString(){
+      StringBuilder sb = new StringBuilder();
+      sb.append( text ).append( '(' ).append( boost ).append( ")(" );
+      for( Toffs to : termsOffsets ){
+        sb.append( to );
+      }
+      sb.append( ')' );
+      return sb.toString();
+    }
+    
+    public static class Toffs {
+      int startOffset;
+      int endOffset;
+      public Toffs( int startOffset, int endOffset ){
+        this.startOffset = startOffset;
+        this.endOffset = endOffset;
+      }
+      void setEndOffset( int endOffset ){
+        this.endOffset = endOffset;
+      }
+      public String toString(){
+        StringBuilder sb = new StringBuilder();
+        sb.append( '(' ).append( startOffset ).append( ',' ).append( endOffset ).append( ')' );
+        return sb.toString();
+      }
+    }
+  }
+}

Property changes on: contrib/fast-vector-highlighter/src/java/org/apache/lucene/search/vectorhighlight/FieldPhraseList.java
___________________________________________________________________
Name: svn:keywords
   + Date Author Id Revision HeadURL
Name: svn:eol-style
   + native

Index: contrib/fast-vector-highlighter/src/java/org/apache/lucene/search/vectorhighlight/FragListBuilder.java
===================================================================
--- contrib/fast-vector-highlighter/src/java/org/apache/lucene/search/vectorhighlight/FragListBuilder.java	(revision 0)
+++ contrib/fast-vector-highlighter/src/java/org/apache/lucene/search/vectorhighlight/FragListBuilder.java	(revision 0)
@@ -0,0 +1,34 @@
+package org.apache.lucene.search.vectorhighlight;
+
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * FragListBuilder is an interface for FieldFragList builder classes.
+ * A FragListBuilder class can be plugged in to Highlighter.
+ */
+public interface FragListBuilder {
+
+  /**
+   * create a FieldFragList.
+   * 
+   * @param fieldPhraseList FieldPhraseList object
+   * @param fragCharSize the length (number of chars) of a fragment
+   * @return the created FieldFragList object
+   */
+  public FieldFragList createFieldFragList( FieldPhraseList fieldPhraseList, int fragCharSize );
+}

Property changes on: contrib/fast-vector-highlighter/src/java/org/apache/lucene/search/vectorhighlight/FragListBuilder.java
___________________________________________________________________
Name: svn:keywords
   + Date Author Id Revision HeadURL
Name: svn:eol-style
   + native

Index: contrib/fast-vector-highlighter/src/java/org/apache/lucene/search/vectorhighlight/ScoreOrderFragmentsBuilder.java
===================================================================
--- contrib/fast-vector-highlighter/src/java/org/apache/lucene/search/vectorhighlight/ScoreOrderFragmentsBuilder.java	(revision 0)
+++ contrib/fast-vector-highlighter/src/java/org/apache/lucene/search/vectorhighlight/ScoreOrderFragmentsBuilder.java	(revision 0)
@@ -0,0 +1,69 @@
+package org.apache.lucene.search.vectorhighlight;
+
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.List;
+
+import org.apache.lucene.search.vectorhighlight.FieldFragList.WeightedFragInfo;
+
+/**
+ * An implementation of FragmentsBuilder that outputs score-order fragments.
+ */
+public class ScoreOrderFragmentsBuilder extends BaseFragmentsBuilder {
+
+  /**
+   * a constructor.
+   */
+  public ScoreOrderFragmentsBuilder(){
+    super();
+  }
+
+  /**
+   * a constructor.
+   * 
+   * @param preTags aray of pre-tags for markup terms.
+   * @param postTags array of post-tags for markup terms.
+   */
+  public ScoreOrderFragmentsBuilder( String[] preTags, String[] postTags ){
+    super( preTags, postTags );
+  }
+
+  /**
+   * Sort by score the list of WeightedFragInfo
+   */
+  public List<WeightedFragInfo> getWeightedFragInfoList( List<WeightedFragInfo> src ) {
+    Collections.sort( src, new ScoreComparator() );
+    return src;
+  }
+
+  public static class ScoreComparator implements Comparator<WeightedFragInfo> {
+
+    public int compare( WeightedFragInfo o1, WeightedFragInfo o2 ) {
+      if( o1.totalBoost > o2.totalBoost ) return -1;
+      else if( o1.totalBoost < o2.totalBoost ) return 1;
+      // if same score then check startOffset
+      else{
+        if( o1.startOffset < o2.startOffset ) return -1;
+        else if( o1.startOffset > o2.startOffset ) return 1;
+      }
+      return 0;
+    }
+  }
+}

Property changes on: contrib/fast-vector-highlighter/src/java/org/apache/lucene/search/vectorhighlight/ScoreOrderFragmentsBuilder.java
___________________________________________________________________
Name: svn:keywords
   + Date Author Id Revision HeadURL
Name: svn:eol-style
   + native

Index: contrib/fast-vector-highlighter/src/java/org/apache/lucene/search/vectorhighlight/SimpleFragmentsBuilder.java
===================================================================
--- contrib/fast-vector-highlighter/src/java/org/apache/lucene/search/vectorhighlight/SimpleFragmentsBuilder.java	(revision 0)
+++ contrib/fast-vector-highlighter/src/java/org/apache/lucene/search/vectorhighlight/SimpleFragmentsBuilder.java	(revision 0)
@@ -0,0 +1,53 @@
+package org.apache.lucene.search.vectorhighlight;
+
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import java.util.List;
+
+import org.apache.lucene.search.vectorhighlight.FieldFragList.WeightedFragInfo;
+
+/**
+ * A simple implementation of FragmentsBuilder.
+ *
+ */
+public class SimpleFragmentsBuilder extends BaseFragmentsBuilder {
+
+  /**
+   * a constructor.
+   */
+  public SimpleFragmentsBuilder() {
+    super();
+  }
+
+  /**
+   * a constructor.
+   * 
+   * @param preTags array of pre-tags for markup terms.
+   * @param postTags array of post-tags for markup terms.
+   */
+  public SimpleFragmentsBuilder( String[] preTags, String[] postTags ) {
+    super( preTags, postTags );
+  }
+
+  /**
+   * do nothing. return the source list.
+   */
+  public List<WeightedFragInfo> getWeightedFragInfoList( List<WeightedFragInfo> src ) {
+    return src;
+  }
+}

Property changes on: contrib/fast-vector-highlighter/src/java/org/apache/lucene/search/vectorhighlight/SimpleFragmentsBuilder.java
___________________________________________________________________
Name: svn:keywords
   + Date Author Id Revision HeadURL
Name: svn:eol-style
   + native

Index: contrib/fast-vector-highlighter/src/java/org/apache/lucene/search/vectorhighlight/BaseFragmentsBuilder.java
===================================================================
--- contrib/fast-vector-highlighter/src/java/org/apache/lucene/search/vectorhighlight/BaseFragmentsBuilder.java	(revision 0)
+++ contrib/fast-vector-highlighter/src/java/org/apache/lucene/search/vectorhighlight/BaseFragmentsBuilder.java	(revision 0)
@@ -0,0 +1,124 @@
+package org.apache.lucene.search.vectorhighlight;
+
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+
+import org.apache.lucene.document.Document;
+import org.apache.lucene.document.MapFieldSelector;
+import org.apache.lucene.index.IndexReader;
+import org.apache.lucene.search.vectorhighlight.FieldFragList.WeightedFragInfo;
+import org.apache.lucene.search.vectorhighlight.FieldFragList.WeightedFragInfo.SubInfo;
+import org.apache.lucene.search.vectorhighlight.FieldPhraseList.WeightedPhraseInfo.Toffs;
+
+public abstract class BaseFragmentsBuilder implements FragmentsBuilder {
+
+  protected String[] preTags, postTags;
+  public static final String[] COLORED_PRE_TAGS = {
+    "<b style=\"background:yellow\">", "<b style=\"background:lawngreen\">", "<b style=\"background:aquamarine\">",
+    "<b style=\"background:magenta\">", "<b style=\"background:palegreen\">", "<b style=\"background:coral\">",
+    "<b style=\"background:wheat\">", "<b style=\"background:khaki\">", "<b style=\"background:lime\">",
+    "<b style=\"background:deepskyblue\">"
+  };
+  public static final String[] COLORED_POST_TAGS = { "</b>" };
+  
+  protected BaseFragmentsBuilder(){
+    this( new String[]{ "<b>" }, new String[]{ "</b>" } );
+  }
+  
+  protected BaseFragmentsBuilder( String[] preTags, String[] postTags ){
+    this.preTags = preTags;
+    this.postTags = postTags;
+  }
+  
+  static Object checkTagsArgument( Object tags ){
+    if( tags instanceof String ) return tags;
+    else if( tags instanceof String[] ) return tags;
+    throw new IllegalArgumentException( "type of preTags/postTags must be a String or String[]" );
+  }
+  
+  public abstract List<WeightedFragInfo> getWeightedFragInfoList( List<WeightedFragInfo> src );
+  
+  public String createFragment( IndexReader reader, int docId,
+      String fieldName, FieldFragList fieldFragList ) throws IOException {
+    String[] fragments = createFragments( reader, docId, fieldName, fieldFragList, 1 );
+    if( fragments == null || fragments.length == 0 ) return null;
+    return fragments[0];
+  }
+
+  public String[] createFragments( IndexReader reader, int docId,
+      String fieldName, FieldFragList fieldFragList, int maxNumFragments )
+      throws IOException {
+    if( maxNumFragments < 0 )
+      throw new IllegalArgumentException( "maxNumFragments(" + maxNumFragments + ") must be positive number." );
+
+    List<WeightedFragInfo> fragInfos = getWeightedFragInfoList( fieldFragList.fragInfos );
+    
+    List<String> fragments = new ArrayList<String>( maxNumFragments );
+    String[] values = getFieldValues( reader, docId, fieldName );
+    StringBuilder buffer = new StringBuilder();
+    int[] nextValueIndex = { 0 };
+    for( int n = 0; n < maxNumFragments && n < fragInfos.size(); n++ ){
+      WeightedFragInfo fragInfo = fragInfos.get( n );
+      fragments.add( makeFragment( buffer, nextValueIndex, values, fragInfo ) );
+    }
+    return fragments.toArray( new String[fragments.size()] );
+  }
+  
+  protected String[] getFieldValues( IndexReader reader, int docId, String fieldName) throws IOException {
+    Document doc = reader.document( docId, new MapFieldSelector( new String[]{ fieldName } ) );
+    return doc.getValues( fieldName );
+  }
+
+  protected String makeFragment( StringBuilder buffer, int[] index, String[] values, WeightedFragInfo fragInfo ){
+    StringBuilder fragment = new StringBuilder();
+    final int s = fragInfo.startOffset;
+    String src = getFragmentSource( buffer, index, values, s, fragInfo.endOffset );
+    int srcIndex = 0;
+    for( SubInfo subInfo : fragInfo.subInfos ){
+      for( Toffs to : subInfo.termsOffsets ){
+        fragment.append( src.substring( srcIndex, to.startOffset - s ) ).append( getPreTag( subInfo.seqnum ) )
+          .append( src.substring( to.startOffset - s, to.endOffset - s ) ).append( getPostTag( subInfo.seqnum ) );
+        srcIndex = to.endOffset - s;
+      }
+    }
+    fragment.append( src.substring( srcIndex ) );
+    return fragment.toString();
+  }
+  
+  protected String getFragmentSource( StringBuilder buffer, int[] index, String[] values,
+      int startOffset, int endOffset ){
+    while( buffer.length() < endOffset && index[0] < values.length ){
+      if( index[0] > 0 && values[index[0]].length() > 0 )
+        buffer.append( ' ' );
+      buffer.append( values[index[0]++] );
+    }
+    int eo = buffer.length() < endOffset ? buffer.length() : endOffset;
+    return buffer.substring( startOffset, eo );
+  }
+  
+  protected String getPreTag( int num ){
+    return preTags.length > num ? preTags[num] : preTags[0];
+  }
+  
+  protected String getPostTag( int num ){
+    return postTags.length > num ? postTags[num] : postTags[0];
+  }
+}

Property changes on: contrib/fast-vector-highlighter/src/java/org/apache/lucene/search/vectorhighlight/BaseFragmentsBuilder.java
___________________________________________________________________
Name: svn:keywords
   + Date Author Id Revision HeadURL
Name: svn:eol-style
   + native

Index: contrib/fast-vector-highlighter/src/java/org/apache/lucene/search/vectorhighlight/FieldQuery.java
===================================================================
--- contrib/fast-vector-highlighter/src/java/org/apache/lucene/search/vectorhighlight/FieldQuery.java	(revision 0)
+++ contrib/fast-vector-highlighter/src/java/org/apache/lucene/search/vectorhighlight/FieldQuery.java	(revision 0)
@@ -0,0 +1,391 @@
+package org.apache.lucene.search.vectorhighlight;
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import org.apache.lucene.index.Term;
+import org.apache.lucene.search.BooleanClause;
+import org.apache.lucene.search.BooleanQuery;
+import org.apache.lucene.search.PhraseQuery;
+import org.apache.lucene.search.Query;
+import org.apache.lucene.search.TermQuery;
+import org.apache.lucene.search.vectorhighlight.FieldTermStack.TermInfo;
+
+/**
+ * FieldQuery breaks down query object into terms/phrases and keep
+ * them in QueryPhraseMap structure.
+ */
+public class FieldQuery {
+
+  final boolean fieldMatch;
+
+  // fieldMatch==true,  Map<fieldName,QueryPhraseMap>
+  // fieldMatch==false, Map<null,QueryPhraseMap>
+  Map<String, QueryPhraseMap> rootMaps = new HashMap<String, QueryPhraseMap>();
+
+  // fieldMatch==true,  Map<fieldName,setOfTermsInQueries>
+  // fieldMatch==false, Map<null,setOfTermsInQueries>
+  Map<String, Set<String>> termSetMap = new HashMap<String, Set<String>>();
+
+  int termOrPhraseNumber; // used for colored tag support
+
+  FieldQuery( Query query, boolean phraseHighlight, boolean fieldMatch ){
+    this.fieldMatch = fieldMatch;
+    Set<Query> flatQueries = new HashSet<Query>();
+    flatten( query, flatQueries );
+    saveTerms( flatQueries );
+    Collection<Query> expandQueries = expand( flatQueries );
+
+    for( Query flatQuery : expandQueries ){
+      QueryPhraseMap rootMap = getRootMap( flatQuery );
+      rootMap.add( flatQuery );
+      if( !phraseHighlight && flatQuery instanceof PhraseQuery ){
+        PhraseQuery pq = (PhraseQuery)flatQuery;
+        if( pq.getTerms().length > 1 ){
+          for( Term term : pq.getTerms() )
+            rootMap.addTerm( term, flatQuery.getBoost() );
+        }
+      }
+    }
+  }
+  
+  void flatten( Query sourceQuery, Collection<Query> flatQueries ){
+    if( sourceQuery instanceof BooleanQuery ){
+      BooleanQuery bq = (BooleanQuery)sourceQuery;
+      for( BooleanClause clause : bq.getClauses() ){
+        if( !clause.isProhibited() )
+          flatten( clause.getQuery(), flatQueries );
+      }
+    }
+    else if( sourceQuery instanceof TermQuery ){
+      if( !flatQueries.contains( sourceQuery ) )
+        flatQueries.add( sourceQuery );
+    }
+    else if( sourceQuery instanceof PhraseQuery ){
+      if( !flatQueries.contains( sourceQuery ) ){
+        PhraseQuery pq = (PhraseQuery)sourceQuery;
+        if( pq.getTerms().length > 1 )
+          flatQueries.add( pq );
+        else if( pq.getTerms().length == 1 ){
+          flatQueries.add( new TermQuery( pq.getTerms()[0] ) );
+        }
+      }
+    }
+    // else discard queries
+  }
+  
+  /*
+   * Create expandQueries from flatQueries.
+   * 
+   * expandQueries := flatQueries + overlapped phrase queries
+   * 
+   * ex1) flatQueries={a,b,c}
+   *      => expandQueries={a,b,c}
+   * ex2) flatQueries={a,"b c","c d"}
+   *      => expandQueries={a,"b c","c d","b c d"}
+   */
+  Collection<Query> expand( Collection<Query> flatQueries ){
+    Set<Query> expandQueries = new HashSet<Query>();
+    for( Iterator<Query> i = flatQueries.iterator(); i.hasNext(); ){
+      Query query = i.next();
+      i.remove();
+      expandQueries.add( query );
+      if( !( query instanceof PhraseQuery ) ) continue;
+      for( Iterator<Query> j = flatQueries.iterator(); j.hasNext(); ){
+        Query qj = j.next();
+        if( !( qj instanceof PhraseQuery ) ) continue;
+        checkOverlap( expandQueries, (PhraseQuery)query, (PhraseQuery)qj );
+      }
+    }
+    return expandQueries;
+  }
+
+  /*
+   * Check if PhraseQuery A and B have overlapped part.
+   * 
+   * ex1) A="a b", B="b c" => overlap; expandQueries={"a b c"}
+   * ex2) A="b c", B="a b" => overlap; expandQueries={"a b c"}
+   * ex3) A="a b", B="c d" => no overlap; expandQueries={}
+   */
+  private void checkOverlap( Collection<Query> expandQueries, PhraseQuery a, PhraseQuery b ){
+    if( a.getSlop() != b.getSlop() ) return;
+    Term[] ats = a.getTerms();
+    Term[] bts = b.getTerms();
+    if( fieldMatch && !ats[0].field().equals( bts[0].field() ) ) return;
+    checkOverlap( expandQueries, ats, bts, a.getSlop(), a.getBoost() );
+    checkOverlap( expandQueries, bts, ats, b.getSlop(), b.getBoost() );
+  }
+
+  /*
+   * Check if src and dest have overlapped part and if it is, create PhraseQueries and add expandQueries.
+   * 
+   * ex1) src="a b", dest="c d"       => no overlap
+   * ex2) src="a b", dest="a b c"     => no overlap
+   * ex3) src="a b", dest="b c"       => overlap; expandQueries={"a b c"}
+   * ex4) src="a b c", dest="b c d"   => overlap; expandQueries={"a b c d"}
+   * ex5) src="a b c", dest="b c"     => no overlap
+   * ex6) src="a b c", dest="b"       => no overlap
+   * ex7) src="a a a a", dest="a a a" => overlap;
+   *                                     expandQueries={"a a a a a","a a a a a a"}
+   */
+  private void checkOverlap( Collection<Query> expandQueries, Term[] src, Term[] dest, int slop, float boost ){
+    // beginning from 1 (not 0) is safe because that the PhraseQuery has multiple terms
+    // is guaranteed in flatten() method (if PhraseQuery has only one term, flatten()
+    // converts PhraseQuery to TermQuery)
+    for( int i = 1; i < src.length; i++ ){
+      boolean overlap = true;
+      for( int j = i; j < src.length; j++ ){
+        if( !src[j].text().equals( dest[j-i].text() ) ){
+          overlap = false;
+          break;
+        }
+      }
+      if( overlap && src.length - i < dest.length ){
+        PhraseQuery pq = new PhraseQuery();
+        for( Term srcTerm : src )
+          pq.add( srcTerm );
+        for( int k = src.length - i; k < dest.length; k++ ){
+          pq.add( new Term( src[0].field(), dest[k].text() ) );
+        }
+        pq.setSlop( slop );
+        pq.setBoost( boost );
+        if(!expandQueries.contains( pq ) )
+          expandQueries.add( pq );
+      }
+    }
+  }
+  
+  QueryPhraseMap getRootMap( Query query ){
+    String key = getKey( query );
+    QueryPhraseMap map = rootMaps.get( key );
+    if( map == null ){
+      map = new QueryPhraseMap( this );
+      rootMaps.put( key, map );
+    }
+    return map;
+  }
+  
+  /*
+   * Return 'key' string. 'key' is the field name of the Query.
+   * If not fieldMatch, 'key' will be null.
+   */
+  private String getKey( Query query ){
+    if( !fieldMatch ) return null;
+    if( query instanceof TermQuery )
+      return ((TermQuery)query).getTerm().field();
+    else if ( query instanceof PhraseQuery ){
+      PhraseQuery pq = (PhraseQuery)query;
+      Term[] terms = pq.getTerms();
+      return terms[0].field();
+    }
+    else
+      throw new RuntimeException( "query \"" + query.toString() + "\" must be flatten first." );
+  }
+
+  /*
+   * Save the set of terms in the queries to termSetMap.
+   * 
+   * ex1) q=name:john
+   *      - fieldMatch==true
+   *          termSetMap=Map<"name",Set<"john">>
+   *      - fieldMatch==false
+   *          termSetMap=Map<null,Set<"john">>
+   *          
+   * ex2) q=name:john title:manager
+   *      - fieldMatch==true
+   *          termSetMap=Map<"name",Set<"john">,
+   *                         "title",Set<"manager">>
+   *      - fieldMatch==false
+   *          termSetMap=Map<null,Set<"john","manager">>
+   *          
+   * ex3) q=name:"john lennon"
+   *      - fieldMatch==true
+   *          termSetMap=Map<"name",Set<"john","lennon">>
+   *      - fieldMatch==false
+   *          termSetMap=Map<null,Set<"john","lennon">>
+   */
+  void saveTerms( Collection<Query> flatQueries ){
+    for( Query query : flatQueries ){
+      Set<String> termSet = getTermSet( query );
+      if( query instanceof TermQuery )
+        termSet.add( ((TermQuery)query).getTerm().text() );
+      else if( query instanceof PhraseQuery ){
+        for( Term term : ((PhraseQuery)query).getTerms() )
+          termSet.add( term.text() );
+      }
+      else
+        throw new RuntimeException( "query \"" + query.toString() + "\" must be flatten first." );
+    }
+  }
+  
+  private Set<String> getTermSet( Query query ){
+    String key = getKey( query );
+    Set<String> set = termSetMap.get( key );
+    if( set == null ){
+      set = new HashSet<String>();
+      termSetMap.put( key, set );
+    }
+    return set;
+  }
+  
+  Set<String> getTermSet( String field ){
+    return termSetMap.get( fieldMatch ? field : null );
+  }
+
+  /**
+   * 
+   * @param fieldName
+   * @param term
+   * @return
+   */
+  public QueryPhraseMap getFieldTermMap( String fieldName, String term ){
+    QueryPhraseMap rootMap = getRootMap( fieldName );
+    return rootMap == null ? null : rootMap.subMap.get( term );
+  }
+
+  /**
+   * 
+   * @param fieldName
+   * @param phraseCandidate
+   * @return
+   */
+  public QueryPhraseMap searchPhrase( String fieldName, final List<TermInfo> phraseCandidate ){
+    QueryPhraseMap root = getRootMap( fieldName );
+    if( root == null ) return null;
+    return root.searchPhrase( phraseCandidate );
+  }
+  
+  private QueryPhraseMap getRootMap( String fieldName ){
+    return rootMaps.get( fieldMatch ? fieldName : null );
+  }
+  
+  int nextTermOrPhraseNumber(){
+    return termOrPhraseNumber++;
+  }
+  
+  public static class QueryPhraseMap {
+
+    boolean terminal;
+    int slop;   // valid if terminal == true and phraseHighlight == true
+    float boost;  // valid if terminal == true
+    int termOrPhraseNumber;   // valid if terminal == true
+    FieldQuery fieldQuery;
+    Map<String, QueryPhraseMap> subMap = new HashMap<String, QueryPhraseMap>();
+    
+    public QueryPhraseMap( FieldQuery fieldQuery ){
+      this.fieldQuery = fieldQuery;
+    }
+
+    void addTerm( Term term, float boost ){
+      QueryPhraseMap map = getOrNewMap( subMap, term.text() );
+      map.markTerminal( boost );
+    }
+    
+    private QueryPhraseMap getOrNewMap( Map<String, QueryPhraseMap> subMap, String term ){
+      QueryPhraseMap map = subMap.get( term );
+      if( map == null ){
+        map = new QueryPhraseMap( fieldQuery );
+        subMap.put( term, map );
+      }
+      return map;
+    }
+
+    void add( Query query ){
+      if( query instanceof TermQuery ){
+        addTerm( ((TermQuery)query).getTerm(), query.getBoost() );
+      }
+      else if( query instanceof PhraseQuery ){
+        PhraseQuery pq = (PhraseQuery)query;
+        Term[] terms = pq.getTerms();
+        Map<String, QueryPhraseMap> map = subMap;
+        QueryPhraseMap qpm = null;
+        for( Term term : terms ){
+          qpm = getOrNewMap( map, term.text() );
+          map = qpm.subMap;
+        }
+        qpm.markTerminal( pq.getSlop(), pq.getBoost() );
+      }
+      else
+        throw new RuntimeException( "query \"" + query.toString() + "\" must be flatten first." );
+    }
+    
+    public QueryPhraseMap getTermMap( String term ){
+      return subMap.get( term );
+    }
+    
+    private void markTerminal( float boost ){
+      markTerminal( 0, boost );
+    }
+    
+    private void markTerminal( int slop, float boost ){
+      this.terminal = true;
+      this.slop = slop;
+      this.boost = boost;
+      this.termOrPhraseNumber = fieldQuery.nextTermOrPhraseNumber();
+    }
+    
+    public boolean isTerminal(){
+      return terminal;
+    }
+    
+    public int getSlop(){
+      return slop;
+    }
+    
+    public float getBoost(){
+      return boost;
+    }
+    
+    public int getTermOrPhraseNumber(){
+      return termOrPhraseNumber;
+    }
+    
+    public QueryPhraseMap searchPhrase( final List<TermInfo> phraseCandidate ){
+      QueryPhraseMap currMap = this;
+      for( TermInfo ti : phraseCandidate ){
+        currMap = currMap.subMap.get( ti.getText() );
+        if( currMap == null ) return null;
+      }
+      return currMap.isValidTermOrPhrase( phraseCandidate ) ? currMap : null;
+    }
+    
+    public boolean isValidTermOrPhrase( final List<TermInfo> phraseCandidate ){
+      // check terminal
+      if( !terminal ) return false;
+
+      // if the candidate is a term, it is valid
+      if( phraseCandidate.size() == 1 ) return true;
+
+      // else check whether the candidate is valid phrase
+      // compare position-gaps between terms to slop
+      int pos = phraseCandidate.get( 0 ).getPosition();
+      for( int i = 1; i < phraseCandidate.size(); i++ ){
+        int nextPos = phraseCandidate.get( i ).getPosition();
+        if( Math.abs( nextPos - pos - 1 ) > slop ) return false;
+        pos = nextPos;
+      }
+      return true;
+    }
+  }
+}

Property changes on: contrib/fast-vector-highlighter/src/java/org/apache/lucene/search/vectorhighlight/FieldQuery.java
___________________________________________________________________
Name: svn:keywords
   + Date Author Id Revision HeadURL
Name: svn:eol-style
   + native

Index: contrib/fast-vector-highlighter/src/java/org/apache/lucene/search/vectorhighlight/SimpleFragListBuilder.java
===================================================================
--- contrib/fast-vector-highlighter/src/java/org/apache/lucene/search/vectorhighlight/SimpleFragListBuilder.java	(revision 0)
+++ contrib/fast-vector-highlighter/src/java/org/apache/lucene/search/vectorhighlight/SimpleFragListBuilder.java	(revision 0)
@@ -0,0 +1,82 @@
+package org.apache.lucene.search.vectorhighlight;
+
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+
+import org.apache.lucene.search.vectorhighlight.FieldPhraseList.WeightedPhraseInfo;
+
+/**
+ * A simple implementation of FragListBuilder.
+ */
+public class SimpleFragListBuilder implements FragListBuilder {
+  
+  public static final int MARGIN = 6;
+  public static final int MIN_FRAG_CHAR_SIZE = MARGIN * 3;
+
+  public FieldFragList createFieldFragList(FieldPhraseList fieldPhraseList, int fragCharSize) {
+    if( fragCharSize < MIN_FRAG_CHAR_SIZE )
+      throw new IllegalArgumentException( "fragCharSize(" + fragCharSize + ") is too small. It must be " +
+          MIN_FRAG_CHAR_SIZE + " or higher." );
+
+    FieldFragList ffl = new FieldFragList( fragCharSize );
+
+    List<WeightedPhraseInfo> wpil = new ArrayList<WeightedPhraseInfo>();
+    Iterator<WeightedPhraseInfo> ite = fieldPhraseList.phraseList.iterator();
+    WeightedPhraseInfo phraseInfo = null;
+    int startOffset = 0;
+    boolean taken = false;
+    while( true ){
+      if( !taken ){
+        if( !ite.hasNext() ) break;
+        phraseInfo = ite.next();
+      }
+      taken = false;
+      if( phraseInfo == null ) break;
+
+      // if the phrase violates the border of previous fragment, discard it and try next phrase
+      if( phraseInfo.getStartOffset() < startOffset ) continue;
+
+      wpil.clear();
+      wpil.add( phraseInfo );
+      int st = phraseInfo.getStartOffset() - MARGIN < startOffset ?
+          startOffset : phraseInfo.getStartOffset() - MARGIN;
+      int en = st + fragCharSize;
+      startOffset = en;
+
+      while( true ){
+        if( ite.hasNext() ){
+          phraseInfo = ite.next();
+          taken = true;
+          if( phraseInfo == null ) break;
+        }
+        else
+          break;
+        if( phraseInfo.getEndOffset() <= en )
+          wpil.add( phraseInfo );
+        else
+          break;
+      }
+      ffl.add( st, en, wpil );
+    }
+    return ffl;
+  }
+
+}

Property changes on: contrib/fast-vector-highlighter/src/java/org/apache/lucene/search/vectorhighlight/SimpleFragListBuilder.java
___________________________________________________________________
Name: svn:keywords
   + Date Author Id Revision HeadURL
Name: svn:eol-style
   + native

Index: contrib/fast-vector-highlighter/src/java/org/apache/lucene/search/vectorhighlight/FragmentsBuilder.java
===================================================================
--- contrib/fast-vector-highlighter/src/java/org/apache/lucene/search/vectorhighlight/FragmentsBuilder.java	(revision 0)
+++ contrib/fast-vector-highlighter/src/java/org/apache/lucene/search/vectorhighlight/FragmentsBuilder.java	(revision 0)
@@ -0,0 +1,57 @@
+package org.apache.lucene.search.vectorhighlight;
+
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import java.io.IOException;
+
+import org.apache.lucene.index.IndexReader;
+
+/**
+ * FragmentsBuilder is an interface for fragments (snippets) builder classes.
+ * A FragmentsBuilder class can be plugged in to Highlighter.
+ */
+public interface FragmentsBuilder {
+
+  /**
+   * create a fragment.
+   * 
+   * @param reader IndexReader of the index
+   * @param docId document id to be highlighted
+   * @param fieldName field of the document to be highlighted
+   * @param fieldFragList FieldFragList object
+   * @return a created fragment or null when no fragment created
+   * @throws IOException
+   */
+  public String createFragment( IndexReader reader, int docId, String fieldName,
+      FieldFragList fieldFragList ) throws IOException;
+
+  /**
+   * create multiple fragments.
+   * 
+   * @param reader IndexReader of the index
+   * @param docId document id to be highlighter
+   * @param fieldName field of the document to be highlighted
+   * @param fieldFragList FieldFragList object
+   * @param maxNumFragments maximum number of fragments
+   * @return created fragments or null when no fragments created.
+   *         size of the array can be less than maxNumFragments
+   * @throws IOException
+   */
+  public String[] createFragments( IndexReader reader, int docId, String fieldName,
+      FieldFragList fieldFragList, int maxNumFragments ) throws IOException;
+}

Property changes on: contrib/fast-vector-highlighter/src/java/org/apache/lucene/search/vectorhighlight/FragmentsBuilder.java
___________________________________________________________________
Name: svn:keywords
   + Date Author Id Revision HeadURL
Name: svn:eol-style
   + native

Index: contrib/fast-vector-highlighter/src/java/org/apache/lucene/search/vectorhighlight/package.html
===================================================================
--- contrib/fast-vector-highlighter/src/java/org/apache/lucene/search/vectorhighlight/package.html	(revision 0)
+++ contrib/fast-vector-highlighter/src/java/org/apache/lucene/search/vectorhighlight/package.html	(revision 0)
@@ -0,0 +1,126 @@
+<html>
+<body>
+This is an another highlighter implementation.
+
+<h2>Features</h2>
+<ul>
+<li>fast for large docs</li>
+<li>support N-gram fields</li>
+<li>support phrase-unit highlighting with slops</li>
+<li>need Java 1.5</li>
+<li>highlight fields need to be TermVector.WITH_POSITIONS_OFFSETS</li>
+<li>take into account query boost to score fragments</li>
+<li>support colored highlight tags</li>
+<li>pluggable FragListBuilder</li>
+<li>pluggable FragmentsBuilder</li>
+</ul>
+
+<h2>Algorithm</h2>
+<p>To explain the algorithm, let's use the following sample text
+ (to be highlighted) and user query:</p>
+
+<table border=1>
+<tr>
+<td><b>Sample Text</b></td>
+<td>Lucene is a search engine library.</td>
+</tr>
+<tr>
+<td><b>User Query</b></td>
+<td>Lucene^2 OR "search library"~1</td>
+</tr>
+</table>
+
+<p>The user query is a BooleanQuery that consists of TermQuery("Lucene") 
+with boost of 2 and PhraseQuery("search library") with slop of 1.</p>
+<p>For your convenience, here is the offsets and positions info of the 
+sample text.</p>
+
+<pre>
++--------+-----------------------------------+
+|        |          1111111111222222222233333|
+|  offset|01234567890123456789012345678901234|
++--------+-----------------------------------+
+|document|Lucene is a search engine library. |
++--------*-----------------------------------+
+|position|0      1  2 3      4      5        |
++--------*-----------------------------------+
+</pre>
+
+<h3>Step 1.</h3>
+<p>In Step 1, Fast Vector Highlighter generates {@link org.apache.lucene.search.vectorhighlight.FieldQuery.QueryPhraseMap} from the user query.
+<code>QueryPhraseMap</code> consists of the following members:</p>
+<pre>
+public class QueryPhraseMap {
+  boolean terminal;
+  int slop;   // valid if terminal == true and phraseHighlight == true
+  float boost;  // valid if terminal == true
+  Map&lt;String, QueryPhraseMap&gt; subMap;
+} 
+</pre>
+<p><code>QueryPhraseMap</code> has subMap. The key of the subMap is a term 
+text in the user query and the value is a subsequent <code>QueryPhraseMap</code>.
+If the query is a term (not phrase), then the subsequent <code>QueryPhraseMap</code>
+is marked as terminal. If the query is a phrase, then the subsequent <code>QueryPhraseMap</code>
+is not a terminal and it has the next term text in the phrase.</p>
+
+<p>From the sample user query, the following <code>QueryPhraseMap</code> 
+will be generated:</p>
+<pre>
+   QueryPhraseMap
++--------+-+  +-------+-+
+|"Lucene"|o+->|boost=2|*|  * : terminal
++--------+-+  +-------+-+
+
++--------+-+  +---------+-+  +-------+------+-+
+|"search"|o+->|"library"|o+->|boost=1|slop=1|*|
++--------+-+  +---------+-+  +-------+------+-+
+</pre>
+
+<h3>Step 2.</h3>
+<p>In Step 2, Fast Vector Highlighter generates {@link org.apache.lucene.search.vectorhighlight.FieldTermStack}. Fast Vector Highlighter uses {@link org.apache.lucene.index.TermFreqVector} data
+(must be stored {@link org.apache.lucene.document.Field.TermVector#WITH_POSITIONS_OFFSETS})
+to generate it. <code>FieldTermStack</code> keeps the terms in the user query.
+Therefore, in this sample case, Fast Vector Highlighter generates the following <code>FieldTermStack</code>:</p>
+<pre>
+   FieldTermStack
++------------------+
+|"Lucene"(0,6,0)   |
++------------------+
+|"search"(12,18,3) |
++------------------+
+|"library"(26,33,5)|
++------------------+
+where : "termText"(startOffset,endOffset,position)
+</pre>
+<h3>Step 3.</h3>
+<p>In Step 3, Fast Vector Highlighter generates {@link org.apache.lucene.search.vectorhighlight.FieldPhraseList}
+by reference to <code>QueryPhraseMap</code> and <code>FieldTermStack</code>.</p>
+<pre>
+   FieldPhraseList
++----------------+-----------------+---+
+|"Lucene"        |[(0,6)]          |w=2|
++----------------+-----------------+---+
+|"search library"|[(12,18),(26,33)]|w=1|
++----------------+-----------------+---+
+</pre>
+<p>The type of each entry is <code>WeightedPhraseInfo</code> that consists of
+an array of terms offsets and weight. The weight (Fast Vector Highlighter uses query boost to
+calculate the weight) will be taken into account when Fast Vector Highlighter creates
+{@link org.apache.lucene.search.vectorhighlight.FieldFragList} in the next step.</p>
+<h3>Step 4.</h3>
+<p>In Step 4, Fast Vector Highlighter creates <code>FieldFragList</code> by reference to
+<code>FieldPhraseList</code>. In this sample case, the following
+<code>FieldFragList</code> will be generated:</p>
+<pre>
+   FieldFragList
++---------------------------------+
+|"Lucene"[(0,6)]                  |
+|"search library"[(12,18),(26,33)]|
+|totalBoost=3                     |
++---------------------------------+
+</pre>
+<h3>Step 5.</h3>
+<p>In Step 5, by using <code>FieldFragList</code> and the field stored data,
+Fast Vector Highlighter creates highlighted snippets!</p>
+</body>
+</html>

Property changes on: contrib/fast-vector-highlighter/src/java/org/apache/lucene/search/vectorhighlight/package.html
___________________________________________________________________
Name: svn:keywords
   + Date Author Id Revision HeadURL
Name: svn:eol-style
   + native

Index: contrib/fast-vector-highlighter/build.xml
===================================================================
--- contrib/fast-vector-highlighter/build.xml	(revision 0)
+++ contrib/fast-vector-highlighter/build.xml	(revision 0)
@@ -0,0 +1,47 @@
+<?xml version="1.0"?>
+
+<!--
+    Licensed to the Apache Software Foundation (ASF) under one or more
+    contributor license agreements.  See the NOTICE file distributed with
+    this work for additional information regarding copyright ownership.
+    The ASF licenses this file to You under the Apache License, Version 2.0
+    the "License"); you may not use this file except in compliance with
+    the License.  You may obtain a copy of the License at
+ 
+        http://www.apache.org/licenses/LICENSE-2.0
+ 
+    Unless required by applicable law or agreed to in writing, software
+    distributed under the License is distributed on an "AS IS" BASIS,
+    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+    See the License for the specific language governing permissions and
+    limitations under the License.
+ -->
+
+<project name="fast-vector-highlighter" default="default">
+
+  <description>
+    Hits highlighter using TermVectors
+  </description>
+
+  <property name="javac.source" value="1.5" />
+  <property name="javac.target" value="1.5" />
+
+  <import file="../contrib-build.xml"/>
+
+  <property name="analyzers.jar" location="${common.dir}/build/contrib/analyzers/lucene-analyzers-${version}.jar"/>
+  <available property="analyzers.jar.present" type="file" file="${analyzers.jar}"/>
+
+  <path id="classpath">
+    <pathelement path="${lucene.jar}"/>
+    <pathelement path="${analyzers.jar}"/>
+    <pathelement path="${project.classpath}"/>
+  </path>
+
+  <target name="compile-core" depends="build-analyzers, common.compile-core" />
+
+  <target name="build-analyzers" unless="analyzers.jar.present">
+    <echo>Fast Vector Highlighter building dependency ${analyzers.jar}</echo>
+    <ant antfile="../analyzers/build.xml" target="default" inheritall="false" dir="../analyzers" />
+  </target>
+
+</project>

Property changes on: contrib/fast-vector-highlighter/build.xml
___________________________________________________________________
Name: svn:keywords
   + Date Author Id Revision HeadURL
Name: svn:eol-style
   + native

